1
\$\begingroup\$

I wanted to make a single threaded network client that only uses a single connection to a server. IOCP seems like overkill but overlapped IO with event notification I think is the most performant of the myriad of choices available. However I haven't found any examples of such so this is my version. I'm still in learning mode so while the code seems to work fine, there are probably gotchas, inefficiencies and potential errors which I'm hoping will be pointed out to me by those of greater learning than I.

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <winsock2.h>

#define DATA_BUFSIZE 4096
#define ERROR_END0 {WSACleanup(); return 1;}
#define ERROR_END1 {closesocket(s); WSACleanup(); return 1;}

int main(int argc, char *argv[])
{
    WSADATA wsa;
    struct hostent *he;
    char *hostname = "my.echo.server.com";
    short port = 12345;

    printf("\nInitialising Winsock...");
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)  { printf("Failed. Error Code : %d", WSAGetLastError()); ERROR_END0; }
    puts("Initialised.");

    if ((he = gethostbyname(hostname)) == NULL) { printf("gethostbyname failed : %d", WSAGetLastError()); ERROR_END0; } 
    printf("%s resolved to : %s\n", hostname, inet_ntoa(*((struct in_addr *) he->h_addr)));

    struct sockaddr_in sockAddr = { AF_INET, htons(port) };
    memcpy(&sockAddr.sin_addr, he->h_addr, he->h_length);

    SOCKET s = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); 
    if(s == INVALID_SOCKET) { printf("Could not create socket : %d" , WSAGetLastError()); ERROR_END0; }
    puts("Socket created.");

    if (WSAConnect(s , (SOCKADDR*)&sockAddr , sizeof(sockAddr), NULL, NULL, NULL, NULL) ==  SOCKET_ERROR)   { puts("connect error"); ERROR_END1; }
    printf("Connected\n\n");

    WSAEVENT EventArray[2];
    WSAOVERLAPPED TXOverlapped = {0}, RXOverlapped = {0};

    TXOverlapped.hEvent = EventArray[0] = WSACreateEvent(); 
    RXOverlapped.hEvent = EventArray[1] = WSACreateEvent();

    char txBuffer[DATA_BUFSIZE], rxBuffer[DATA_BUFSIZE];
    DWORD numBytes, Flags = 0;
    strcpy(txBuffer, "hello world");
    WSABUF txBuff = {sizeof("hello world"), txBuffer}, rxBuff = {sizeof(rxBuffer), rxBuffer};
    if ((WSASend(s, &txBuff, 1, &numBytes, 0, &TXOverlapped, NULL) == SOCKET_ERROR) && (WSAGetLastError() != WSA_IO_PENDING))   { printf("WSASend() failed with error %d\n", WSAGetLastError()); ERROR_END1; }
    if ((WSARecv(s, &rxBuff, 1, &numBytes, &Flags, &RXOverlapped, NULL) == SOCKET_ERROR) && (WSAGetLastError() != WSA_IO_PENDING)) { printf("WSARecv() failed with error %d\n", WSAGetLastError()); ERROR_END1; }

    while(TRUE)
    {
        DWORD Index = WSAWaitForMultipleEvents(2, EventArray, FALSE, WSA_INFINITE, FALSE);
        WSAResetEvent( EventArray[Index] );                                         
        if(Index == 0)
        {
            WSAGetOverlappedResult(s, &TXOverlapped, &numBytes, FALSE, &Flags);
            if (numBytes == 0)  { printf("Closing socket %d\n", (int) s); ERROR_END1; }
            txBuffer[numBytes] = 0;
            printf("TX Bytes SENT: %d\n\n%s\n\n", (int) numBytes, txBuffer);    
        }
        else if(Index == 1)
        {
            WSAGetOverlappedResult(s, &RXOverlapped, &numBytes, FALSE, &Flags);
            if (numBytes == 0)  { printf("Closing socket %d\n", (int) s); ERROR_END1; }
            rxBuffer[numBytes] = 0;
            printf("RX Bytes RECEIVED: %d\n\n%s\n\n", (int) numBytes, rxBuffer);

            ZeroMemory(&RXOverlapped, sizeof(WSAOVERLAPPED));
            RXOverlapped.hEvent = EventArray[1];
            if ((WSARecv(s, &rxBuff, 1, &numBytes, &Flags, &RXOverlapped, NULL) == SOCKET_ERROR) && (WSAGetLastError() != WSA_IO_PENDING)) { printf("WSARecv() failed with error %d\n", WSAGetLastError()); ERROR_END1; }
        }
    }
    shutdown(s,SD_BOTH);
    closesocket(s);
    WSACleanup();
    return 0;
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.