Selaa lähdekoodia

Added windows specific socket timeout code (#1714)

* Added windows specific socket timeout code

Added socket code for windows that respects our given timeout.
Also added some more ifndef checks to some stat reporting functions.
Updated the MSDN reference link.
Fixed the delay being milliseconds on windows, thanks to @theultramage
Fixes #794 on windows.
Lemongrass3110 8 vuotta sitten
vanhempi
commit
b0bcd060c4
1 muutettua tiedostoa jossa 47 lisäystä ja 6 poistoa
  1. 47 6
      src/common/socket.c

+ 47 - 6
src/common/socket.c

@@ -302,18 +302,22 @@ void setsocketopts(int fd,int delay_timeout){
 	sSetsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&yes, sizeof(yes));
 
 	// force the socket into no-wait, graceful-close mode (should be the default, but better make sure)
-	//(http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/closesocket_2.asp)
+	//(https://msdn.microsoft.com/en-us/library/windows/desktop/ms737582%28v=vs.85%29.aspx)
 	{
-	struct linger opt;
-	opt.l_onoff = 0; // SO_DONTLINGER
-	opt.l_linger = 0; // Do not care
-	if( sSetsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&opt, sizeof(opt)) )
-		ShowWarning("setsocketopts: Unable to set SO_LINGER mode for connection #%d!\n", fd);
+		struct linger opt;
+		opt.l_onoff = 0; // SO_DONTLINGER
+		opt.l_linger = 0; // Do not care
+		if( sSetsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&opt, sizeof(opt)) )
+			ShowWarning("setsocketopts: Unable to set SO_LINGER mode for connection #%d!\n", fd);
 	}
 	if(delay_timeout){
+#if defined(WIN32)
+		int timeout = delay_timeout * 1000;
+#else
 		struct timeval timeout;
 		timeout.tv_sec = delay_timeout;
 		timeout.tv_usec = 0;
+#endif
 
 		if (sSetsockopt (fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,sizeof(timeout)) < 0)
 			ShowError("setsocketopts: Unable to set SO_RCVTIMEO timeout for connection #%d!\n");
@@ -568,7 +572,42 @@ int make_connection(uint32 ip, uint16 port, bool silent,int timeout) {
 
 	if( !silent )
 		ShowStatus("Connecting to %d.%d.%d.%d:%i\n", CONVIP(ip), port);
+#ifdef WIN32
+	// On Windows we have to set the socket non-blocking before the connection to make timeout work. [Lemongrass]
+	set_nonblocking(fd, 1);
+
+	result = sConnect(fd, (struct sockaddr *)(&remote_address), sizeof(struct sockaddr_in));
+
+	// Only enter if a socket error occurred
+	// Create a pseudo scope to be able to break out in case of successful connection
+	while( result == SOCKET_ERROR ) {
+		// Specially handle the error number for connection attempts that would block, because we want to use a timeout
+		if( sErrno == S_EWOULDBLOCK ){
+			fd_set writeSet;
+			struct timeval tv;
+
+			sFD_ZERO(&writeSet);
+			sFD_SET(fd,&writeSet);
+
+			tv.tv_sec = timeout;
+			tv.tv_usec = 0;
+
+			// Try to find out if the socket is writeable yet(within the timeout) and check if it is really writeable afterwards
+			if( sSelect(0, NULL, &writeSet, NULL, &tv) != 0 && sFD_ISSET(fd, &writeSet) != 0 ){
+				// Our socket is writeable now => we have connected successfully
+				break; // leave the pseudo scope
+			}
+			// Our connection attempt timed out or the socket was not writeable
+		}
+
+		if( !silent )
+			ShowError("make_connection: connect failed (socket #%d, %s)!\n", fd, error_msg());
 
+		do_close(fd);
+		return -1;
+	}
+	// Keep the socket in non-blocking mode, since we would set it to non-blocking here on unix. [Lemongrass]
+#else
 	result = sConnect(fd, (struct sockaddr *)(&remote_address), sizeof(struct sockaddr_in));
 	if( result == SOCKET_ERROR ) {
 		if( !silent )
@@ -576,8 +615,10 @@ int make_connection(uint32 ip, uint16 port, bool silent,int timeout) {
 		do_close(fd);
 		return -1;
 	}
+
 	//Now the socket can be made non-blocking. [Skotlex]
 	set_nonblocking(fd, 1);
+#endif
 
 	if (fd_max <= fd) fd_max = fd + 1;
 	sFD_SET(fd,&readfds);