October 4th, 2004, 12:08 PM
Visual C++ Network: How do I use a timeout for 'connect()'?
Q: How do I use a timeout for 'connect()'?
A: Before discussing how to use a timeout for the 'connect()' function, it will be good taking a look at what exactly happens at the time of establishing a TCP connection. When a TCP connection is initiated, a packet with a SYN flag is send (client) to the remote PC (server). Once the server gets the SYN packet it is acknowledged with an ACK packet. Although the connection is bi-directional, it requires synchronization from both sides. Thus, the stack responds again with a SYN packet. Instead of responding twice, the server combines both ACK and SYN into a single packet and send it as a SYN/ACK packet. Finally, the client sends an ACK packet back, acknowledging the SYN from the server. This process is commonly known as the TCP 'Three-Way Handshake'.
Now we will look at what happens when the server is not responding. When the client issues a 'connect()', the stack sends a SYN packet. Since the server never gets the SYN packet or does not respond to it, the client never gets SYN/ACK packet. So the client waits for some time, thinking that the packet might be lost on its way, and sends a SYN packet again. This process continues for sometime before the stack abandons the effort and 'connect()' returns with 'WSAETIMEDOUT'.
Now, the initial timeout value - before the first connection attempt - is 3 seconds. After each retry, the amount of time to wait is doubled (3 seconds, 6 seconds etc.). The number of retries can be configured via the 'TcpMaxConnectRetransmissions' parameter in the registry - the default is set to 3 for Windows NT and 2 for Windows 2000. Thus, the first solution to the connection timeout problem is, to adjust this parameter via the Registry. However, changing the registry is a global change, affecting all applications that use Winsock and/or TCP/IP. The following Knowledge Base article provides detailed information. Nevertheless, one should be careful while playing with these parameters.
Winsock API functions such as 'send()', 'recv()', 'connect()' etc. block because we are using blocking sockets. The next solution is to simply use non-blocking sockets instead. For a non-blocking socket, 'connect()' returns with 'WSAEWOULDBLOCK' error. However, the connection process goes on. Thus, one crude but simple way of adding a timeout is to issue a 'connect()' call and wait for a specified amount of time, then call a winsock function like 'getpeername()' or 'recv()' that needs a connected socket. If the error is 'WSAENOTCONN' we can safely assume that the socket is not connected and close the socket. The drawback of this method is that even if the connection occurs quickly, it will only be known after the specified timeout. We can reduce this effect by checking at intervals and abandoning the effort once the total time has elapsed.
Originally Posted by MSDN
Another way to add a timeout to non-blocking sockets is to use notification functions such as
Since the usage of these functions need a great amount of discussion, it will not be attempted here. You are sure to find the usage on the net and other books and also in MSDN which should be the first place of reference.
Yet another method is to use 'ConnectEx()'. Since this API is a Microsoft specific winsock2 extension, you have to include 'mswsock.h' and link to the library 'mswsock.lib'. 'ConnectEx()' is the only function that allows overlapped calls with 'connect()'. However, this API is available only in Windows XP and later. Lets look at the usage with some example code.
In the above declarations the variable 'ci' needs some explanation. The structure 'ContextInfo' contains the context information of the overlapped call. You can assign anything you think would be necessary to maintain the context info. Here, we are assigning only the index number. The structure is defined as
int addrLen = sizeof(sockAddr);
DWORD dwBytes = 0;
The parameter 'ol' is the overlapped parameter which we use to make an overlapped operation. Here, we are loading the 'ConnectEx()' function at run-time. This becomes necessary if we can't find the API declaration in the 'mswsock.h' file. This kind of loading is also practiced for Microsoft specific extension functions to avoid performance penalty at the time of calling. For this purpose we declare two parameters
Once the socket is created, we have to bind it to a port and interface. While using 'ConnectEx()', binding has to be done explicitly because the main purpose of this API other than allowing overlapped calls is, performance.
LPFN_CONNECTEX lpfnConnectEx = NULL; // a pointer to the 'ConnectEx()' function
GUID GuidConnectEx = WSAID_CONNECTEX; // The Guid
Most of the function parameters are self-explanatory. The IO control code 'SIO_GET_EXTENSION_FUNCTION_PONTER' retrieves a pointer to the specified extension function, which is 'ConnectEx()' in our case. The input buffer contains a GUID whose value identifies the function 'ConnectEx()'.
dwErr = WSAIoctl(cliSock,
if(dwErr == SOCKET_ERROR)
// Handle the error using 'WSAGetLastError()'
There is nothing really hard in this API call. Most of the parameters are again self-explanatory. The fourth parameter 'lpSendBuffer' is an optional parameter which points to the buffer to send as soon as the connection is established. If this function is used synchronously, it returns only when the buffer has been sent. The number of bytes sent is returned in the 6th parameter, 'BytesSent'. If the request is successful and not completed immediately, which is quite less likely for connection establishment, we get the error 'ERROR_IO_PENDING' since we initiated an overlapped connection request.
ci.index = 1;
bRet = lpfnConnectEx(cliSock,
dwErr = WSAGetLastError();
if(dwErr != ERROR_IO_PENDING)
// Handle the error
Now all that is left is to get the overlapped result. There are different ways to get the overlapped result. You can use any one of them according to the preference.
The 'WSAGetOverlappedResult()' has a parameter 'fWait' that indicates whether the function will complete immediately or has to wait till the overlapped IO is complete. If you are polling in order to know whether or not the IO has completed then the first function 'HasOverlappedIoCompleted()' offers high performance test operation. You can also use the 'hEvent' parameter of the OVERLAPPED structure to get the event notification of the overlapped operation, thereby using the wait functions. If you are using IO completion port, then you have 'GetQueuedCompletionStatus()' to get the status of the overlapped IO operation.
Of the above mentioned functions, the first two utilizes the poll method, with the drawbacks mentioned earlier. The last two functions get signaled as soon as the connection is complete and will also return if there is a timeout. However using the wait functions or 'HasOverlappedIoCompleted()' requires you to get the overlapped result by the function 'WSAGetOverlappedResult()'.
Once we get the result of the overlapped function, we can use other members of the 'ContextInfo' structure to get the information associated with the socket or the particular IO.
bRet = WSAGetOverlappedResult(cliSock,
// Handle the error using WSAGetLastError
if(ci.dwIndex == 1) // first connection is successful
Last edited by Andreas Masur; July 25th, 2005 at 03:59 PM.
Click Here to Expand Forum to Full Width