diff --git a/src/common/socket.c b/src/common/socket.c index 45ed164d9e..314aa23f0d 100644 --- a/src/common/socket.c +++ b/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);