nitzanms nitzanms - 7 months ago 82
C Question

WSAConnectByName timeout

I'm trying to use

to connect to an address. However, it seems like it disregards the

Here is the (only slightly modified) code from MS's example:

int iResult;
BOOL bSuccess;
SOCKADDR_STORAGE RemoteAddr = {0};
DWORD dwLocalAddr = sizeof(LocalAddr);
DWORD dwRemoteAddr = sizeof(RemoteAddr);

ConnSocket = socket(AF_INET, SOCK_STREAM, 0);
if (ConnSocket == INVALID_SOCKET){
wprintf(L"socket failed with error: %d\n", WSAGetLastError());

struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 0;

bSuccess = WSAConnectByName(ConnSocket, NodeName,
PortName, &dwLocalAddr,
(struct timeval *)&tv,
if (!bSuccess){
wprintf(L"WsaConnectByName failed with error: %d\n", WSAGetLastError());

When I use an address that doesn't exist (like a local IP address), instead of failing with a timeout, the code stops until some other timeout occurs.

Any idea what's going on here?


This is due to bad documentation.

The timeout parameter is only partially used. WSAConnectByName is a complex function, which performs many operations internally.

At the beginning exists code like this:

ULONG time, ms;
if (timeout)
  time = GetTickCount();
  ms = timeout->tv_sec * 1000 + timeout->tv_usec/1000;

and then several times it calls code like this:

if (timeout && GetTickCount() - time > ms) return WSAETIMEDOUT;

but the heart of the function calls to ConnectEx like this:

if (!ConnectEx(*))
  if (GetLastError() == ERROR_IO_PENDING)
    WSAGetOverlappedResult(*); // you wait here and the timeout is not used!

Both ConnectEx (which is an asynchronous function) and GetOverlappedResult have no parameter for specifying a timeout. You end up waiting in GetOverlappedResult after ConnectEx exits, but you can not set a timeout for it.

There exists only one nice solution - use ConnectEx directly and not use any timeouts.

else one problem point - GetAddrInfoEx used to translate nodename to ip address. this function called with timeout=0, lpOverlapped=0, lpCompletionRoutine = 0 - so here also can wait on DNS request. asynchronous query supported only begin from Windows 8


if direct use ConnectEx we can use timeout (thank Remy Lebeau for idea) - create/use OVERLAPPED.hEvent and

OVERLAPPED Overlapped = {};
Overlapped.hEvent = CreateEvent(0, 0, 0, 0);
//... ConnectEx ...
ULONG NumberOfBytesTransferred = 0;
ULONG err = GetLastError();
if (err == ERROR_IO_PENDING)
    switch (WaitForSingleObject(Overlapped.hEvent, ms))
        CancelIoEx((HANDLE)s, &Overlapped);
        err = WSAETIMEDOUT;
    case WAIT_OBJECT_0:
        // really final NT status of operation is Internal and NumberOfBytesTransferred == InternalHigh
        // GetOverlappedResult set LastError by translating (NTSTATUS)Internal to win32 error
        // with the loss of accuracy, especially if  status > 0
        GetOverlappedResult((HANDLE)s, &Overlapped, &NumberOfBytesTransferred, TRUE);
        err = GetLastError();

        // code of GetOverlappedResult in this case for clarity
        //if ((NTSTATUS)Overlapped.InternalHigh == STATUS_PENDING)
        //  WaitForSingleObject(Overlapped.hEvent, INFINITE);
        //NumberOfBytesTransferred = (ULONG)Overlapped.InternalHigh;
        //if (0 > (NTSTATUS)Overlapped.Internal) 
        //  SetLastError(RtlNtStatusToDosError((NTSTATUS)Overlapped.Internal));

    default: __debugbreak();

or for alternative use GetOverlappedResultEx with our timeout (but need windows 8+)

or the best choice (for my view) - use BindIoCompletionCallback((HANDLE)s,) or direct bind self IOCP for socket and not wait after call at all.