I have two code examples of simple WinSock programs that listen for connections using an Event object associated with a listening socket. Before reading further, check these two code snippets and guess (without running them) what you think will happen in both.
Code snippet 1:
int main() {
WSADATA wsaData;
int res = WSAStartup(MAKEWORD(2, 2), &wsaData);
// ... error handling omitted
int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// ... error handling omitted
// Make the socket non-blocking
u_long mode = 1;
res = ioctlsocket(sock, FIONBIO, &mode);
// ... error handling omitted
// Bind the socket to a specific address and port
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(7878);
if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
// ... error handling omitted
}
// Listen for incoming connections
if (listen(sock, 10) == SOCKET_ERROR) {
// ... error handling omitted
}
// Wait for incoming connections
// Create a WSAEVENT object
WSAEVENT event = WSACreateEvent();
// ... error handling omitted
// Associate the WSAEVENT object with the socket
res = WSAEventSelect(sock, event, FD_ACCEPT);
// ... error handling omitted
// Wait for an event on the socket
res = WaitForMultipleObjects(1, &event, FALSE, INFINITE);
// ... error handling omitted
/**
* Closing the event object here before using the socket and without
* disassociating it with the socket
*/
WSACloseEvent(event);
int conn_sock = accept(sock, NULL, NULL);
// ... error handling omitted
printf("New connection\n");
closesocket(conn_sock);
printf("Connection closed\n");
closesocket(sock);
WSACleanup();
return 0;
}
Code snippet 2:
int main() {
WSADATA wsaData;
int res = WSAStartup(MAKEWORD(2, 2), &wsaData);
// ... error handling omitted
int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// ... error handling omitted
// Make the socket non-blocking
u_long mode = 1;
res = ioctlsocket(sock, FIONBIO, &mode);
// ... error handling omitted
// Bind the socket to a specific address and port
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(7878);
if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
// ... error handling omitted
}
// Listen for incoming connections
if (listen(sock, 10) == SOCKET_ERROR) {
// ... error handling omitted
}
// Wait for incoming connections
// Create a WSAEVENT object
WSAEVENT event = WSACreateEvent();
// ... error handling omitted
// Associate the WSAEVENT object with the socket
res = WSAEventSelect(sock, event, FD_ACCEPT);
// ... error handling omitted
// Wait for an event on the socket
res = WaitForMultipleObjects(1, &event, FALSE, INFINITE);
// ... error handling omitted
/**
* Closing the event object after disassociating it from the socket, but
* before using the socket
*/
res = WSAEventSelect(sock, event, 0);
WSACloseEvent(event);
int conn_sock = accept(sock, NULL, NULL);
if (conn_sock == INVALID_SOCKET) {
printf("accept failed with error: %d\n", WSAGetLastError());
closesocket(sock);
WSACleanup();
return 1;
}
printf("New connection\n");
closesocket(conn_sock);
printf("Connection closed\n");
closesocket(sock);
WSACleanup();
return 0;
}
If you had guessed that the first snippet would crash, you would be right. Apparently, if you associate an event object with a socket, closing the event object will lead to operations on the socket returning WSAENOTSOCK error. If you dissociate the event object from the socket first, you can use it without problems.
I can't find any references in the documentation to this behaviour (I checked the WSAEventSelect and WSACloseEvent documentation). I know it may seem simple, but I discovered this in a more complex codebase where reproducing and tracing it was much more difficult.
Did you know this? Are there any more quirks related to the relationship between EventObjects and Sockets?