ConnectEx requires a “disconnected, previously connected socket”. Indeed, if I omit the bind step in my example (see below), ConnectEx does not work with WSAEINVAL .
Here is my current understanding: before calling ConnectEx , bind the socket to INADDR_ANY and port 0 (if it is not already connected):
struct sockaddr_in addr; ZeroMemory(&addr, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = 0; rc = bind(sock, (SOCKADDR*) &addr, sizeof(addr)); if (rc != 0) { ... bind failed; call WSAGetLastError to see why ... }
Or for an IPv6 socket:
struct sockaddr_in6 addr; ZeroMemory(&addr, sizeof(addr)); addr.sin6_family = AF_INET6; addr.sin6_addr = in6addr_any; addr.sin6_port = 0; rc = bind(sock, (SOCKADDR*) &addr, sizeof(addr)); if (rc != 0) { ... bind failed; call WSAGetLastError to see why ... }
This allows the operating system to assign a local address to our socket (as opposed to the remote address to which we are connecting). connect does this step automatically, but ConnectEx does not.
My questions:
Is my rating correct?
Is there a way to do this automatic binding, which is agnostic for the address family, or will I have to process each of AF_INET , AF_INET6 , AF_BTH (Bluetooth), etc. manually?
An example of a working ConnectEx (also in Gist: https://gist.github.com/4158972 ):
#include <stdio.h> #include <WinSock2.h> #include <MSWSock.h> #include <WS2tcpip.h> #pragma comment(lib, "Ws2_32.lib") struct mswsock_s { LPFN_CONNECTEX ConnectEx; } mswsock; static BOOL load_mswsock(void) { SOCKET sock; DWORD dwBytes; int rc; /* Dummy socket needed for WSAIoctl */ sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == INVALID_SOCKET) return FALSE; { GUID guid = WSAID_CONNECTEX; rc = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), &mswsock.ConnectEx, sizeof(mswsock.ConnectEx), &dwBytes, NULL, NULL); if (rc != 0) return FALSE; } rc = closesocket(sock); if (rc != 0) return FALSE; return TRUE; } int main(int argc, char *argv[]) { int rc; BOOL ok; WSADATA wsaData; SOCKET sock; rc = WSAStartup(MAKEWORD(2,2), &wsaData); if (rc != 0) { printf("WSAStartup failed: %d\n", rc); return 1; } if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { printf("Your computer is from the wrong millenium.\n"); WSACleanup(); return 1; } if (!load_mswsock()) { printf("Error loading mswsock functions: %d\n", WSAGetLastError()); return 1; } sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == INVALID_SOCKET) { printf("socket: %d\n", WSAGetLastError()); return 1; } /* ConnectEx requires the socket to be initially bound. */ { struct sockaddr_in addr; ZeroMemory(&addr, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = 0; rc = bind(sock, (SOCKADDR*) &addr, sizeof(addr)); if (rc != 0) { printf("bind failed: %d\n", WSAGetLastError()); return 1; } } /* Issue ConnectEx and wait for the operation to complete. */ { OVERLAPPED ol; ZeroMemory(&ol, sizeof(ol)); sockaddr_in addr; ZeroMemory(&addr, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr("173.194.37.36"); // google.com addr.sin_port = htons(80); ok = mswsock.ConnectEx(sock, (SOCKADDR*) &addr, sizeof(addr), NULL, 0, NULL, &ol); if (ok) { printf("ConnectEx succeeded immediately\n"); } else if (WSAGetLastError() == ERROR_IO_PENDING) { printf("ConnectEx pending\n"); DWORD numBytes; ok = GetOverlappedResult((HANDLE) sock, &ol, &numBytes, TRUE); if (ok) printf("ConnectEx succeeded\n"); else printf("ConnectEx failed: %d\n", WSAGetLastError()); } else { printf("ConnectEx failed: %d\n", WSAGetLastError()); return 1; } } /* Make the socket more well-behaved. */ rc = setsockopt(sock, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0); if (rc != 0) { printf("SO_UPDATE_CONNECT_CONTEXT failed: %d\n", WSAGetLastError()); return 1; } /* This will fail if SO_UPDATE_CONNECT_CONTEXT was not performed. */ rc = shutdown(sock, SD_BOTH); if (rc != 0) { printf("shutdown failed: %d\n", WSAGetLastError()); return 1; } printf("Done\n"); return 0; }