Home > database >  Winsock "recv" doesn't return on a non-graceful connection termination
Winsock "recv" doesn't return on a non-graceful connection termination

Time:09-27

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.

  • Related