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;
}