changeset 11557:39f9d11c789a draft

(svn r15922) -Codechange: unify the ways to listen on a socket
author rubidium <rubidium@openttd.org>
date Fri, 03 Apr 2009 01:24:52 +0000
parents e3b2f1cda105
children 93f83100b0bb
files src/network/core/address.cpp src/network/core/address.h src/network/core/udp.cpp src/network/core/udp.h src/network/network.cpp src/network/network_udp.cpp
diffstat 6 files changed, 88 insertions(+), 62 deletions(-) [+]
line wrap: on
line diff
--- a/src/network/core/address.cpp
+++ b/src/network/core/address.cpp
@@ -82,7 +82,7 @@
 	int e = getaddrinfo(this->GetHostname(), port_name, &hints, &ai);
 	if (e != 0) {
 		DEBUG(net, 0, "getaddrinfo failed: %s", gai_strerror(e));
-		return false;
+		return INVALID_SOCKET;
 	}
 
 	SOCKET sock = INVALID_SOCKET;
@@ -111,4 +111,60 @@
 	return sock;
 }
 
+SOCKET NetworkAddress::Listen(int family, int socktype)
+{
+	struct addrinfo *ai;
+	struct addrinfo hints;
+	memset(&hints, 0, sizeof (hints));
+	hints.ai_family   = family;
+	hints.ai_flags    = AI_ADDRCONFIG | AI_PASSIVE;
+	hints.ai_socktype = socktype;
+
+	/* The port needs to be a string. Six is enough to contain all characters + '\0'. */
+	char port_name[6] = { '0' };
+	seprintf(port_name, lastof(port_name), "%u", this->GetPort());
+
+	int e = getaddrinfo(this->GetHostname(), port_name, &hints, &ai);
+	if (e != 0) {
+		DEBUG(net, 0, "getaddrinfo failed: %s", gai_strerror(e));
+		return INVALID_SOCKET;
+	}
+
+	SOCKET sock = INVALID_SOCKET;
+	for (struct addrinfo *runp = ai; runp != NULL; runp = runp->ai_next) {
+		sock = socket(runp->ai_family, runp->ai_socktype, runp->ai_protocol);
+		if (sock == INVALID_SOCKET) {
+			DEBUG(net, 1, "could not create socket: %s", strerror(errno));
+			continue;
+		}
+
+		if (!SetNoDelay(sock)) DEBUG(net, 1, "Setting TCP_NODELAY failed");
+
+		if (bind(sock, runp->ai_addr, runp->ai_addrlen) != 0) {
+			DEBUG(net, 1, "could not bind: %s", strerror(errno));
+			closesocket(sock);
+			sock = INVALID_SOCKET;
+			continue;
+		}
+
+		if (socktype != SOCK_DGRAM && listen(sock, 1) != 0) {
+			DEBUG(net, 1, "could not listen: %s", strerror(errno));
+			closesocket(sock);
+			sock = INVALID_SOCKET;
+			continue;
+		}
+
+		/* Connection succeeded */
+		if (!SetNonBlocking(sock)) DEBUG(net, 0, "Setting non-blocking mode failed");
+
+		this->address_length = runp->ai_addrlen;
+		assert(sizeof(this->address) >= runp->ai_addrlen);
+		memcpy(&this->address, runp->ai_addr, runp->ai_addrlen);
+		break;
+	}
+	freeaddrinfo (ai);
+
+	return sock;
+}
+
 #endif /* ENABLE_NETWORK */
--- a/src/network/core/address.h
+++ b/src/network/core/address.h
@@ -52,8 +52,8 @@
 	 * @param ip the unresolved hostname
 	 * @param port the port
 	 */
-	NetworkAddress(const char *hostname = NULL, uint16 port = 0) :
-		hostname(hostname == NULL ? NULL : strdup(hostname)),
+	NetworkAddress(const char *hostname = "0.0.0.0", uint16 port = 0) :
+		hostname(strdup(hostname)),
 		address_length(0)
 	{
 		memset(&this->address, 0, sizeof(this->address));
@@ -159,6 +159,14 @@
 	 * @return the connected socket or INVALID_SOCKET.
 	 */
 	SOCKET Connect();
+
+	/**
+	 * Make the given socket listen.
+	 * @param family the type of 'protocol' (IPv4, IPv6)
+	 * @param socktype the type of socket (TCP, UDP, etc)
+	 * @return the listening socket or INVALID_SOCKET.
+	 */
+	SOCKET Listen(int family, int socktype);
 };
 
 #endif /* ENABLE_NETWORK */
--- a/src/network/core/udp.cpp
+++ b/src/network/core/udp.cpp
@@ -17,45 +17,26 @@
 
 /**
  * Start listening on the given host and port.
- * @param host      the host (ip) to listen on
- * @param port      the port to listen on
+ * @param address the host to listen on
  * @param broadcast whether to allow broadcast sending/receiving
  * @return true if the listening succeeded
  */
-bool NetworkUDPSocketHandler::Listen(const uint32 host, const uint16 port, const bool broadcast)
+bool NetworkUDPSocketHandler::Listen(NetworkAddress address, bool broadcast)
 {
-	struct sockaddr_in sin;
-
 	/* Make sure socket is closed */
 	this->Close();
 
-	this->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
-	if (!this->IsConnected()) {
-		DEBUG(net, 0, "[udp] failed to start UDP listener");
-		return false;
-	}
-
-	SetNonBlocking(this->sock);
-
-	sin.sin_family = AF_INET;
-	/* Listen on all IPs */
-	sin.sin_addr.s_addr = host;
-	sin.sin_port = htons(port);
-
-	if (bind(this->sock, (struct sockaddr*)&sin, sizeof(sin)) != 0) {
-		DEBUG(net, 0, "[udp] bind failed on %s:%i", inet_ntoa(*(struct in_addr *)&host), port);
-		return false;
-	}
+	this->sock = address.Listen(AF_INET, SOCK_DGRAM);
 
 	if (broadcast) {
 		/* Enable broadcast */
 		unsigned long val = 1;
 #ifndef BEOS_NET_SERVER /* will work around this, some day; maybe. */
-		setsockopt(this->sock, SOL_SOCKET, SO_BROADCAST, (char *) &val , sizeof(val));
+		setsockopt(this->sock, SOL_SOCKET, SO_BROADCAST, (char *) &val, sizeof(val));
 #endif
 	}
 
-	DEBUG(net, 1, "[udp] listening on port %s:%d", inet_ntoa(*(struct in_addr *)&host), port);
+	DEBUG(net, 1, "[udp] listening on port %s", address.GetAddressAsString());
 
 	return true;
 }
--- a/src/network/core/udp.h
+++ b/src/network/core/udp.h
@@ -125,7 +125,7 @@
 	/** On destructing of this class, the socket needs to be closed */
 	virtual ~NetworkUDPSocketHandler() { this->Close(); }
 
-	bool Listen(uint32 host, uint16 port, bool broadcast);
+	bool Listen(NetworkAddress address, bool broadcast);
 	void Close();
 
 	void SendPacket(Packet *p, NetworkAddress *recv);
--- a/src/network/network.cpp
+++ b/src/network/network.cpp
@@ -556,39 +556,20 @@
 /* Set up the listen socket for the server */
 static bool NetworkListen()
 {
-	SOCKET ls;
-	struct sockaddr_in sin;
+	NetworkAddress address(_settings_client.network.server_bind_ip, _settings_client.network.server_port);
+
+	DEBUG(net, 1, "Listening on %s", address.GetAddressAsString());
 
-	DEBUG(net, 1, "Listening on %s:%d", _settings_client.network.server_bind_ip, _settings_client.network.server_port);
-
-	ls = socket(AF_INET, SOCK_STREAM, 0);
+	SOCKET ls = address.Listen(AF_INET, SOCK_STREAM);
 	if (ls == INVALID_SOCKET) {
-		ServerStartError("socket() on listen socket failed");
+		ServerStartError("Could not create listening socket");
 		return false;
 	}
 
-	{ // reuse the socket
-		int reuse = 1;
-		/* The (const char*) cast is needed for windows!! */
-		if (setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) == -1) {
-			ServerStartError("setsockopt() on listen socket failed");
-			return false;
-		}
-	}
-
-	if (!SetNonBlocking(ls)) DEBUG(net, 0, "Setting non-blocking mode failed"); // XXX should this be an error?
-
-	sin.sin_family = AF_INET;
-	sin.sin_addr.s_addr = _network_server_bind_ip;
-	sin.sin_port = htons(_settings_client.network.server_port);
-
-	if (bind(ls, (struct sockaddr*)&sin, sizeof(sin)) != 0) {
-		ServerStartError("bind() failed");
-		return false;
-	}
-
-	if (listen(ls, 1) != 0) {
-		ServerStartError("listen() failed");
+	int reuse = 1;
+	/* The (const char*) cast is needed for windows!! */
+	if (setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) == -1) {
+		ServerStartError("setsockopt() on listen socket failed");
 		return false;
 	}
 
@@ -795,7 +776,7 @@
 
 	/* Try to start UDP-server */
 	_network_udp_server = true;
-	_network_udp_server = _udp_server_socket->Listen(_network_server_bind_ip, _settings_client.network.server_port, false);
+	_network_udp_server = _udp_server_socket->Listen(NetworkAddress(_network_server_bind_ip, _settings_client.network.server_port), false);
 
 	_network_company_states = CallocT<NetworkCompanyState>(MAX_COMPANIES);
 	_network_server = true;
--- a/src/network/network_udp.cpp
+++ b/src/network/network_udp.cpp
@@ -378,7 +378,7 @@
 void NetworkUDPQueryMasterServer()
 {
 	if (!_udp_client_socket->IsConnected()) {
-		if (!_udp_client_socket->Listen(0, 0, true)) return;
+		if (!_udp_client_socket->Listen(NetworkAddress(), true)) return;
 	}
 
 	Packet p(PACKET_UDP_CLIENT_GET_LIST);
@@ -400,7 +400,7 @@
 
 	/* No UDP-socket yet.. */
 	if (!_udp_client_socket->IsConnected()) {
-		if (!_udp_client_socket->Listen(0, 0, true)) return;
+		if (!_udp_client_socket->Listen(NetworkAddress(), true)) return;
 	}
 
 	DEBUG(net, 0, "[udp] searching server");
@@ -448,7 +448,7 @@
 {
 	/* No UDP-socket yet.. */
 	if (!_udp_client_socket->IsConnected()) {
-		if (!_udp_client_socket->Listen(0, 0, true)) return;
+		if (!_udp_client_socket->Listen(NetworkAddress(), true)) return;
 	}
 
 	NetworkUDPQueryServerInfo *info = new NetworkUDPQueryServerInfo(address, manually);
@@ -483,7 +483,7 @@
 
 	/* check for socket */
 	if (!_udp_master_socket->IsConnected()) {
-		if (!_udp_master_socket->Listen(_network_server_bind_ip, 0, false)) return;
+		if (!_udp_master_socket->Listen(NetworkAddress(_network_server_bind_ip, 0), false)) return;
 	}
 
 	if (!ThreadObject::New(NetworkUDPRemoveAdvertiseThread, NULL)) {
@@ -520,7 +520,7 @@
 
 	/* check for socket */
 	if (!_udp_master_socket->IsConnected()) {
-		if (!_udp_master_socket->Listen(_network_server_bind_ip, 0, false)) return;
+		if (!_udp_master_socket->Listen(NetworkAddress(_network_server_bind_ip, 0), false)) return;
 	}
 
 	if (_network_need_advertise) {