1

I have a winsock-server, accepting packets from a local IP, which currently works without using IOCP. I want it to be non-blocking though, working through IOCP. Yes I know about the alternatives (select, WSAAsync etc.), but this won't do it for developing an MMO server.

So here's the question - how do I do this using std::thread and IOCP?

I already know that GetQueuedCompletionStatus() dequeues packets, while PostQueuedCompletionStatus() queues those to the IOCP. Is this the proper way to do it async though?

How can I threat all clients equally on about 10 threads? I thought about receiving UDP packets and processing those while IOCP has something in queue, but packets will be processed by max 10 at a time and I also have an infinite loop in each thread.

The target is creating a game server, capable of holding thousands of clients at the same time.

About the code: netListener() is a class, holding packets received from the listening network interface in a vector. All it does in Receive() is

WSARecvFrom(sockfd, &buffer, 1, &bytesRecv, &flags, (SOCKADDR*)&senderAddr, &size, &overl, 0); std::cout << "\n\nReceived " << bytesRecv << " bytes.\n" << "Packet [" << std::string(buffer.buf, bytesRecv)<< "]\n";*

The code works, buffer shows what I've sent to myself, but I'm not sure whether having only ONE receive() will suffice.

About blocking - yes, I realized that putting listener.Receive() into a separate thread doesn't block the main thread. But imagine this - lots of clients try to send packets, can one receive process them all? Not to mention I was planning to queue an IOCP packet on each receive, but still not sure how to do this properly.

And another question - is it possible to establish a direct connection between a client and another client? If you host a server on a local machine behind NAT and you want it to be accessible from the internet, for example.

Threads:

void Host::threadFunc(int i) {
threadMutex.lock();
for (;;) {
    if (m_Init) {
        if (GetQueuedCompletionStatus(iocp, &bytesReceived, &completionKey, (LPOVERLAPPED*)&overl, WSA_INFINITE)) {
            std::cout << "1 completion packet dequeued, bytes: " << bytesReceived << std::endl;
        }
    }
}

threadMutex.unlock(); }


void Host::createThreads() {
//Create threads
for (unsigned int i = 0; i < SystemInfo.dwNumberOfProcessors; ++i) {
    threads.push_back(std::thread(&Host::threadFunc, this, i));
    if (threads[i].joinable()) threads[i].detach();
}
std::cout << "Threads created: " << threads.size() << std::endl; }

Host

Host::Host() {
using namespace std;
InitWSA();

createThreads();
m_Init = true;
SecureZeroMemory((PVOID)&overl, sizeof(WSAOVERLAPPED));
overl.hEvent = WSACreateEvent();
iocp = CreateIoCompletionPort((HANDLE)sockfd, iocp, 0, threads.size());

listener = netListener(sockfd, overl, 12); //12 bytes buffer size
for (int i = 0; i < 4; ++i) { //IOCP queue test
    if (PostQueuedCompletionStatus(iocp, 150, completionKey, &overl)) {
        std::cout << "1 completion packet queued\n";
    }
}
std::cin.get();
listener.Receive(); //Packet receive test - adds a completion packet n bytes long if client sent one
std::cin.get();}
Jaro
  • 21
  • 4
  • Because UDP is connectionless, you might only be using a single socket. If that's the case, IOCP may be overkill and actually less efficient unless you are trying to integrate it with additional I/O from elsewhere. – Cory Nelson Feb 07 '17 at 18:10
  • Is there any reason to use IOCP for UDP at all, then? Yes, I'm using one socket for all incoming UDP packets, but will the server hold even with thousands of clients sending 500-1500 byte packets every second? – Jaro Feb 07 '17 at 18:50
  • You use IOCP to multiplex I/O from multiple handles. If you aren't taking advantage of multiplexing, then it's just additional overhead. A regular blocking socket will be faster. It shouldn't have any trouble processing thousands of I/Os per second. If you want the absolute best of performance, check out Registered I/O -- but it's almost definitely not needed. – Cory Nelson Feb 07 '17 at 19:00
  • How much time do you have on your hands? These days a viable approach may be writing a wrapper on top of iocp and coroutines. This will plug seamlessly into std::thread if you are cpp-minded. – Sergei Vorobiev Mar 07 '17 at 06:01

0 Answers0