1

I learned how to create sockets using the Windows Message Proc and switched on FD_CONNECT, FD_ACCEPT, FD_CLOSE, etc.. I used: WSAAsyncSelect(socket, WindowHandle, WM_SOCKET, FD_READ | FD_WRITE | FD_CONNECT | FD_CLOSE | FD_ACCEPT).

This let me know when a socket: accepted, closed, received data all without having to poll for it.

Now I'm trying to learn to do the same for console applications but using IOCP.

I have written the following:

#include <Winsock2.h>
#include <Ws2tcpip.h>
#include <Windows.h>
#include <iostream>
#include <thread>
#include <stdexcept>
#include <cstring>

class Socket
{
    private:
        typedef struct
        {
            OVERLAPPED Overlapped;
            WSABUF DataBuf;
            char Buffer[1024];
            unsigned int BytesSent;
            unsigned int BytesReceived;
        } PER_IO_OPERATION_DATA;

        typedef struct
        {
            SOCKET Sock;
        } PER_HANDLE_DATA;


    protected:
        void Close();
        std::function<void(HANDLE)> Worker;

    public:
        Socket();
        bool Start(std::string Address, unsigned int Port, bool Listen);

        void Read(char* Buffer, int bufflen); //reads from a socket.
        void Write(char* Buffer, int bufflen); //writes to a socket.
        bool Accept();  //accepts a socket.

        virtual void OnRead();   //Called when Reading.
        virtual void OnWrite();  //Called when Writing.
        virtual void OnAccept(); //Called when a socket has been accepted.
        virtual void OnConnect();  //Called when connected.
        virtual void OnDisconnect(); //Called when disconnected.
};

bool Socket::Start(std::string Address, unsigned int Port, bool Listen)
{
    WSADATA wsaData;
    SOCKET sock = 0;
    struct sockaddr_in* sockaddr_ipv4;

    //WSA Startup and getaddrinfo, etc.. here..

    //Create IOCP Handle and worker threads.
    HANDLE IOCPPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 0);
    if (!IOCPPort)
    {
        this->Close();
        throw std::runtime_error("Error: Creating IOCP Failed With Error: " + std::to_string(GetLastError()));
    }

    SYSTEM_INFO SystemInfo = {0};
    GetSystemInfo(&SystemInfo);
    for (std::size_t I = 0; I < SystemInfo.dwNumberOfProcessors * 2; ++I)
    {
        std::thread(this->Worker, IOCPPort).detach();
    }

    //Set the socket to overlapped.
    if ((sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
    {
        this->Close();
        throw std::runtime_error("Error: ");
    }

    struct sockaddr_in SockAddr;
    std::memset(&SockAddr, 0, sizeof(SockAddr));
    SockAddr.sin_port = htons(Port);
    SockAddr.sin_family = AF_INET;
    SockAddr.sin_addr.s_addr = (Address == "INADDR_ANY" ? htonl(INADDR_ANY) : inet_addr(Address.c_str()));

    //If it is a server socket being created, bind it.
    if (Listen && (bind(sock, reinterpret_cast<SOCKADDR*>(&SockAddr), sizeof(SockAddr)) == SOCKET_ERROR))
    {
        this->Close();
        throw std::runtime_error("Error: ");
    }

    //If it is a server socket, start listenening.
    if (Listen && (listen(sock, SOMAXCONN) == SOCKET_ERROR))
    {
        this->Close();
        throw std::runtime_error("Error: ");
    }

    //Otherwise it is a client socket so just connected..
    //if(!Listen && (connect(this->socket, reinterpret_cast<SOCKADDR*>(&SockAddr), sizeof(SockAddr)) == SOCKET_ERROR))


    //Associate this socket with a completion port.
    if (CreateIoCompletionPort(reinterpret_cast<HANDLE>(sock), IOCPPort, 0, 0) != IOCPPort)
    {
        this->Close();
        throw std::runtime_error("Error: ");
    }

    //setsockopt(sock, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char *)sdListen, sizeof(sdListen));
}

However, now I'm stuck. I'm not sure what I need to do after creating the IoCompletionPort. How would I know when the socket has been accepted so I can call OnAccept or how can I tell when there is data to be read so I can call OnRead? I've went through all pages on google and I cannot find anything that is similar to OnRead, OnAccept or OnWrite.

I just want to make it scalable and have callbacks for when something happens without using events or message loop. The only thing I saw on google that interested me was IOCP but I'm completely lost. Any ideas what I need to do next?

Brandon
  • 22,723
  • 11
  • 93
  • 186
  • TBH, I'm not sure what to do next either. Maybe tomorrow, when I've sobered up. Meanwhile, can you post the code for 'this->Worker'? – Martin James Nov 10 '13 at 01:42

1 Answers1

0

I wrote some articles on IOCP server design a long time ago and I also have some code you can download which does the basics for you.

You can get the code and find links to the articles from here. They may help you to understand what happens next and how to structure an IOCP client or server.

Len Holgate
  • 21,282
  • 4
  • 45
  • 92