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.
This commit is contained in:
Lemongrass3110 2016-12-17 00:36:23 +01:00 committed by GitHub
parent 25bcb51598
commit b0bcd060c4

View File

@ -302,7 +302,7 @@ 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
@ -311,9 +311,13 @@ void setsocketopts(int fd,int delay_timeout){
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);