In Winsock/Win32, how to make recv() interruptible by ctrl-c without killing process?

Viewed 161

Here's my attempt to create a wrapper around the winsock recv function in blocking mode so that a control-c can interrupt the connection without closing the process.

I get kind of stuck on how to do a "select" to detect if I have a "ctrl-C event" or a "socket recv event".

Any ideas on how to fix this code to get it working? The idea is that, first I call the CtrlC_Init routine to install the ctrl-c handler... then I replace recv with CtrlC_Recv in my tcpip program, and finally, i and press ctrl-c while tailing a file on a remote computer and it will magically stop without exiting my program....

#include <iostream>
#include <atomic>
#include <mutex>
#include <ws2tcpip.h>

using namespace std;

#pragma comment(lib, "ws2_32.lib")

atomic<HANDLE> g_event_ctrlc {0};

BOOL WINAPI CtrlHandler(DWORD fdwCtrlType);

void CtrlC_Init()
{
    g_event_ctrlc = CreateEvent(NULL, FALSE, FALSE, NULL);
    SetConsoleCtrlHandler(CtrlHandler, TRUE);
}
int CtrlC_Recv( SOCKET s, char* buf, int len, int flags)
{
    WSAEVENT event_recv;
    HANDLE   handle_array[2];
    int      handle_count = 0;

    if (g_event_ctrlc != 0) {
        handle_array[handle_count] = g_event_ctrlc;
        handle_count++;
    }

    // Create Event Receive
    event_recv = WSACreateEvent();
    if (event_recv == WSA_INVALID_EVENT) {
        return SOCKET_ERROR;
    }
    WSAEventSelect(s, event_recv, FD_READ | FD_CLOSE);
    handle_array[handle_count] = event_recv;
    handle_count++;

    DWORD rc = WaitForMultipleObjectsEx(
                  handle_count,
                  handle_array,
                  FALSE, INFINITE, FALSE);

    // Close Event_Recv
    WSAEventSelect(s, event_recv, 0);
    CloseHandle(event_recv);

     // Check for Error
     if(rc == WAIT_FAILED) {
        DWORD err = GetLastError();
        cout << "ERROR" <<  err << "\n";
        return SOCKET_ERROR;
    }

    // If Event Triggered
    if(rc >= WAIT_OBJECT_0 && rc < WAIT_OBJECT_0 + handle_count) {
        // Ctrl-C Interrupt Event
        if (g_event_ctrlc != 0) {
            if (handle_array[rc] == g_event_ctrlc) {
                 cout << "CTRL-C CloseSocket\n";
                 closesocket(s);
                 return SOCKET_ERROR;
            }
        }

        // Received Socket
        if (handle_array[rc] == event_recv) {
            return recv(s, buf, len, flags);
        }
    }

    return SOCKET_ERROR;
}
BOOL WINAPI CtrlHandler(DWORD fdwCtrlType)
{
    //NOTE: make sure console app is run from powershell.exe
    //   instead of being spawned by C++ IDE...
    //   some C++ IDEs trap Ctrl-C before it gets to console
    //   preventing this handler from getting triggered.
    if (fdwCtrlType == CTRL_C_EVENT) {
        printf("Ctrl-c\n");

        if (g_event_ctrlc != 0) {
            SetEvent(g_event_ctrlc);
        }

        return TRUE;
    }
    return FALSE;
}
0 Answers
Related