I'm running the following code:
#undef UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
// Need to link with Ws2_32.lib
#pragma comment (lib, "Ws2_32.lib")
// #pragma comment (lib, "Mswsock.lib")
#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "8081"
int __cdecl main(void)
{
WSADATA wsaData;
int iResult;
SOCKET ListenSocket = INVALID_SOCKET;
SOCKET ClientSocket = INVALID_SOCKET;
struct addrinfo* result = NULL;
struct addrinfo hints;
int iSendResult;
char recvbuf[DEFAULT_BUFLEN];
int recvbuflen = DEFAULT_BUFLEN;
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed with error: %d\n", iResult);
return 1;
}
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
// Resolve the server address and port
iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
if (iResult != 0) {
printf("getaddrinfo failed with error: %d\n", iResult);
WSACleanup();
return 1;
}
// Create a SOCKET for connecting to server
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ListenSocket == INVALID_SOCKET) {
printf("socket failed with error: %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
// Setup the TCP listening socket
iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult == SOCKET_ERROR) {
printf("bind failed with error: %d\n", WSAGetLastError());
freeaddrinfo(result);
closesocket(ListenSocket);
WSACleanup();
return 1;
}
freeaddrinfo(result);
iResult = listen(ListenSocket, SOMAXCONN);
if (iResult == SOCKET_ERROR) {
printf("listen failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
while (true)
{
printf("Waiting for connection...\n");
// Accept a client socket
ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET) {
printf("accept failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
else
{
printf("Received connection !\n");
}
// No longer need server socket
//closesocket(ListenSocket);
// Receive until the peer shuts down the connection
do {
printf("Before recv\n");
iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
printf("After recv\n");
if (iResult > 0) {
printf("Bytes received: %d\n", iResult);
recvbuf[iResult] = 0;
// Echo the buffer back to the sender
printf("%s\n", recvbuf);
}
else if (iResult == 0)
printf("Connection closing...\n");
else {
printf("recv failed with error: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
} while (iResult > 0);
printf("After loop !!\n");
iResult = shutdown(ClientSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed with error: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
// cleanup
closesocket(ClientSocket);
//
}
// shutdown the connection since we're done
WSACleanup();
return 0;
}
When a client terminates the connection ungracefully, i.e., just shuts down without calling close()
or anything, the recv()
function is blocking and doesn't return.
I've read in the docs of the recv function that:
If the socket is connection oriented and the remote side has shut down the connection gracefully, and all data has been received, a recv will complete immediately with zero bytes received. If the connection has been reset, a recv will fail with the error WSAECONNRESET.
But that's not the case. In my case, the recv() just blocks the execution. I guess that the docs refer to a situation in which you call recv() AFTER the connection has been closed by the peer, but in my case, the connection is being closed after recv() was called. How can I handle this?
CodePudding user response:
An abnormal connection loss (where network communication just stops, vs a reset which is explicit) takes time for the OS to detect it. TCP is designed to be resilient in case of a network outage. recv()
WILL fail eventually, but it could take up to several minutes for the OS to timeout internally and invalidate the connection. Until that happens, no errors are reported.
If you don't want to wait the necessary time for the OS to invalidate the connection, you will have to enable use of your own timeout in your own code, such as by calling select()
before recv()
, or by using setsockopt(SO_RCVTIMEO)
, WSAIoctl(SIO_KEEPALIVE_VALS)
, etc to make recv()
exit on a timeout. Then you can close the socket if the timeout elapses.