Skip to content

Instantly share code, notes, and snippets.

@brokenprogrammer
Last active February 14, 2022 19:50
Show Gist options
  • Select an option

  • Save brokenprogrammer/7ef31af950fcdd4063faaa1212a97ee3 to your computer and use it in GitHub Desktop.

Select an option

Save brokenprogrammer/7ef31af950fcdd4063faaa1212a97ee3 to your computer and use it in GitHub Desktop.
Reference implementation for Event based Overlapped (Asynchronous) I/O TCP Echo Server.
#define Win32OutputWSAErrorCode printf
#define Win32OutputErrorCode printf
DWORD WINAPI
_Win32NetworkListeningThreadProc(LPVOID lpParameter)
{
win32_mprof_net_state *State = (win32_mprof_net_state *)lpParameter;
win32_mprof_sock_connection *SocketConnection;
DWORD BytesTransferred;
DWORD Flags;
while(TRUE)
{
// NOTE(Oskar): Quit flag has been set so we exit and cleanup.
if (State->EchoThreadQuit)
{
break;
}
DWORD WSAWaitResult = WSAWaitForMultipleEvents(State->NumberOfEvents, State->EventArray, FALSE, WSA_INFINITE, FALSE);
if (WSAWaitResult == WSA_WAIT_FAILED)
{
Win32OutputWSAErrorCode("WSAWait Failed.");
return 0;
}
// NOTE(Oskar): Ignore the case if its Zero because that is a new connection handled
// by other thread.
DWORD EventIndex = WSAWaitResult - WSA_WAIT_EVENT_0;
if ((EventIndex) == 0)
{
WSAResetEvent(State->EventArray[0]);
continue;
}
SocketConnection = State->Connections[EventIndex];
WSAResetEvent(State->EventArray[EventIndex]);
BOOL OverlappedResult = WSAGetOverlappedResult(SocketConnection->Socket, &(SocketConnection->Overlapped), &BytesTransferred, FALSE, &Flags);
if (OverlappedResult == FALSE || BytesTransferred == 0)
{
printf("Closing socket %d\n", (int)SocketConnection->Socket);
if (closesocket(SocketConnection->Socket) == SOCKET_ERROR)
{
Win32OutputWSAErrorCode("Call to closesocket Failed.");
}
GlobalFree(SocketConnection);
WSACloseEvent(State->EventArray[EventIndex]);
// NOTE(Oskar): Move event and socket handles to the back of their respective arrays.
EnterCriticalSection(&State->CriticalSection);
{
if (EventIndex + 1 != State->NumberOfEvents)
{
for (DWORD Index = EventIndex; Index < State->NumberOfEvents; Index++)
{
State->EventArray[Index] = State->EventArray[Index + 1];
State->Connections[Index] = State->Connections[Index + 1];
}
}
State->NumberOfEvents--;
}
LeaveCriticalSection(&State->CriticalSection);
continue;
}
// NOTE(Oskar): Here we check what the last queued operation was.
// NOTE(Oskar): Everything bellow here is basically the echo functionality that you can replace.
if (SocketConnection->Operation == MPROF_SOCK_QUEUED_OP_READ)
{
SocketConnection->BytesReceived = BytesTransferred;
SocketConnection->BytesSent = 0;
}
else if (SocketConnection->Operation == MPROF_SOCK_QUEUED_OP_SEND)
{
SocketConnection->BytesReceived = 0;
SocketConnection->BytesSent += BytesTransferred;
}
// NOTE(Oskar): Send with WSASend. WSASend doesn't guarantee sending everything in one go
// so we continue sending untill all bytes are sent.
if (SocketConnection->BytesReceived > SocketConnection->BytesSent)
{
SocketConnection->Operation = MPROF_SOCK_QUEUED_OP_SEND;
ZeroMemory(&(SocketConnection->Overlapped), sizeof(WSAOVERLAPPED));
SocketConnection->Overlapped.hEvent = State->EventArray[EventIndex];
SocketConnection->DataBuf.buf = SocketConnection->Buffer + SocketConnection->BytesSent;
SocketConnection->DataBuf.len = SocketConnection->BytesReceived - SocketConnection->BytesSent;
if (WSASend(SocketConnection->Socket, &(SocketConnection->DataBuf), 1, NULL, 0, &(SocketConnection->Overlapped), NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != ERROR_IO_PENDING)
{
Win32OutputWSAErrorCode("WSASend Failed.");
return 0;
}
}
}
else
{
// NOTE(Oskar): No more byte to send so we continue with WSARecv.
SocketConnection->Operation = MPROF_SOCK_QUEUED_OP_READ;
Flags = 0;
ZeroMemory(&(SocketConnection->Overlapped), sizeof(WSAOVERLAPPED));
SocketConnection->Overlapped.hEvent = State->EventArray[EventIndex];
SocketConnection->DataBuf.len = MPROF_SOCK_BUFFER_SIZE;
SocketConnection->DataBuf.buf = SocketConnection->Buffer;
if (WSARecv(SocketConnection->Socket, &(SocketConnection->DataBuf), 1, NULL, &Flags, &(SocketConnection->Overlapped), NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != ERROR_IO_PENDING)
{
Win32OutputWSAErrorCode("WSARecv Failed.");
return 0;
}
}
}
}
// NOTE(Oskar): Cleanup
for (int Index = 0; Index < State->NumberOfEvents; ++Index)
{
win32_mprof_sock_connection *Connection = State->Connections[Index];
printf("Closing socket %d\n", (int)SocketConnection->Socket);
if (Connection->Socket != INVALID_SOCKET)
{
closesocket(SocketConnection->Socket);
}
GlobalFree(SocketConnection);
WSACloseEvent(State->EventArray[Index]);
}
return 0;
}
static int
Win32InitializeWinsock()
{
WSADATA WSAData;
if (WSAStartup(MAKEWORD(2, 2), &WSAData) != 0)
{
Win32OutputWSAErrorCode("WSAStartup Failed");
WSACleanup();
return -1;
}
return 0;
}
static win32_mprof_net_state
Win32InitializeNetworking()
{
win32_mprof_net_state Result = {0};
Result.Initialized = 0;
Result.NumberOfEvents = 0;
InitializeCriticalSection(&Result.CriticalSection);
struct addrinfo Hints;
ZeroMemory(&Hints, sizeof(Hints));
Hints.ai_family = AF_INET;
Hints.ai_socktype = SOCK_STREAM;
Hints.ai_protocol = IPPROTO_TCP;
Hints.ai_flags = AI_PASSIVE;
if (getaddrinfo(NULL, MPROF_SOCK_DEFAULT_PORT, &Hints, &Result.AddressInfo) != 0)
{
Win32OutputWSAErrorCode("getaddrinfo Failed");
WSACleanup();
return Result;
}
// NOTE(Oskar): Initialize socket object
Result.ListenSocket = WSASocket(Result.AddressInfo->ai_family,
Result.AddressInfo->ai_socktype,
Result.AddressInfo->ai_protocol,
NULL, 0, WSA_FLAG_OVERLAPPED);
if (Result.ListenSocket == INVALID_SOCKET)
{
Win32OutputWSAErrorCode("WSASocket Failed, unable to create listening socket.");
WSACleanup();
return Result;
}
// NOTE(Oskar): Bind socket to a network address.
if (bind(Result.ListenSocket, Result.AddressInfo->ai_addr, (int)Result.AddressInfo->ai_addrlen) == SOCKET_ERROR)
{
Win32OutputWSAErrorCode("Failed to bind listening socket.");
WSACleanup();
return Result;
}
// NOTE(Oskar): Listen for incoming requests.
if (listen(Result.ListenSocket, MPROF_SOCK_DEFAULT_BACKLOG) == SOCKET_ERROR)
{
Win32OutputWSAErrorCode("Failed to initiate listening on socket.");
WSACleanup();
return Result;
}
Result.Initialized = 1;
return Result;
}
static void
Win32CleanupNetworking(win32_mprof_net_state *State)
{
// NOTE(Oskar): Set quit flag to allow thread to clean up after itself.
State->EchoThreadQuit = 1;
if (State->ListenSocket != INVALID_SOCKET)
{
closesocket(State->ListenSocket);
}
if (State->LastAcceptSocket != INVALID_SOCKET)
{
closesocket(State->LastAcceptSocket);
}
if (State->AddressInfo != NULL)
{
freeaddrinfo(State->AddressInfo);
}
WSACleanup();
}
static void
_Win32SocketConnectionInitialize(win32_mprof_sock_connection *Target, SOCKET LastAcceptSocket)
{
Target->Socket = LastAcceptSocket;
ZeroMemory(&(Target->Overlapped), sizeof(OVERLAPPED));
Target->Operation = MPROF_SOCK_QUEUED_OP_READ;
Target->BytesSent = 0;
Target->BytesReceived = 0;
Target->DataBuf.len = MPROF_SOCK_BUFFER_SIZE;
Target->DataBuf.buf = Target->Buffer;
}
static int
Win32NetworkListen(win32_mprof_net_state *State)
{
if ((State->LastAcceptSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
{
Win32OutputWSAErrorCode("Failed to create Accepting socket.");
WSACleanup();
return -1;
}
if ((State->EventArray[0] = WSACreateEvent()) == WSA_INVALID_EVENT)
{
Win32OutputWSAErrorCode("Failed to create new event object.");
return -1;
}
State->EchoThreadHandle = CreateThread(NULL, 0, _Win32NetworkListeningThreadProc, State, 0, &State->EchoThreadId);
if (State->EchoThreadHandle == NULL)
{
Win32OutputErrorCode("Failed to create thread.");
return -1;
}
// NOTE(Oskar): Loop to accept new incoming connections. Upon a new connection we allocate a new
// socket connection that is then available for our listening thread.
State->NumberOfEvents = 1;
while (TRUE)
{
if ((State->LastAcceptSocket = accept(State->ListenSocket, NULL, NULL)) == INVALID_SOCKET)
{
Win32OutputWSAErrorCode("Failed to accept incoming connection.");
return -1;
}
EnterCriticalSection(&State->CriticalSection);
{
if ((State->Connections[State->NumberOfEvents] = (win32_mprof_sock_connection *) GlobalAlloc(GPTR, sizeof(win32_mprof_sock_connection))) == NULL)
{
Win32OutputErrorCode("Call to global alloc failed.");
return -1;
}
win32_mprof_sock_connection *TargetConnection = State->Connections[State->NumberOfEvents];
_Win32SocketConnectionInitialize(TargetConnection, State->LastAcceptSocket);
if ((TargetConnection->Overlapped.hEvent = State->EventArray[State->NumberOfEvents] = WSACreateEvent()) == WSA_INVALID_EVENT)
{
Win32OutputWSAErrorCode("Failed to create new event object.");
return -1;
}
// Post a WSARecv() request to to begin receiving data on the socket
DWORD Flags;
DWORD RecvBytes;
if (WSARecv(TargetConnection->Socket,
&(TargetConnection->DataBuf), 1, &RecvBytes, &Flags, &(TargetConnection->Overlapped), NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != ERROR_IO_PENDING)
{
Win32OutputWSAErrorCode("Failed to recieve data from socket.");
return -1;
}
}
State->NumberOfEvents++;
}
LeaveCriticalSection(&State->CriticalSection);
// Signal the first event in the event array to tell the worker thread to
// service an additional event in the event array
if (WSASetEvent(State->EventArray[0]) == FALSE)
{
Win32OutputWSAErrorCode("Failed to set event object as signaled.");
return -1;
}
}
return 0;
}
#define MPROF_SOCK_DEFAULT_PORT "27015"
#define MPROF_SOCK_BUFFER_SIZE 4096
#define MPROF_SOCK_MAX_THREAD 4
#define MPROF_SOCK_MAX_IO_REQUESTS 10
#define MPROF_SOCK_DEFAULT_BACKLOG 5
typedef enum _win32_mprof_sock_queued_op
{
MPROF_SOCK_QUEUED_OP_READ = 0x1,
MPROF_SOCK_QUEUED_OP_SEND = 0x2,
} win32_mprof_sock_queued_op;
typedef struct _win32_mprof_sock_connection
{
CHAR Buffer[MPROF_SOCK_BUFFER_SIZE];
WSABUF DataBuf;
SOCKET Socket;
WSAOVERLAPPED Overlapped;
win32_mprof_sock_queued_op Operation;
DWORD BytesSent;
DWORD BytesReceived;
} win32_mprof_sock_connection;
typedef struct _win32_mprof_net_state
{
CRITICAL_SECTION CriticalSection;
struct addrinfo *AddressInfo;
SOCKET ListenSocket;
SOCKET LastAcceptSocket;
HANDLE EchoThreadHandle;
DWORD EchoThreadId;
BOOL EchoThreadQuit;
DWORD NumberOfEvents;
WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];
win32_mprof_sock_connection *Connections[WSA_MAXIMUM_WAIT_EVENTS];
BOOL Initialized;
} win32_mprof_net_state;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment