This is the 2nd post for the IOCP series.
1. Network Programming with IOCP and Thread Pool – Intro
Well, a few months ago, I had some time to do my personal project and I decided to explore IOCP and Thread Pools. I wrote the post about it but haven’t had a chance to update it until this weekend. Finally, I’ve made the example project (echo server & client) which shows how to use IOCP with the original thread pool (i.e. the old thread pool before Windows Vista came out. )
I’m not going to explain all the details about IOCP or “the old” Thread Pool APIs since there are already a ton of articles and examples (go google!). Instead I will show a big picture of my programs and point some keys and tips when using the APIs.
Let’s see the client first.
I should mention that you can create hundreds of clients by setting the number via command line option. (see the source code). I use BindIoCompletionCallback() function to handle all I/O events for sockets. To maximize IOCP power, I also decide to use ConnectEx() function instead of nonblocking connect() or WSAConnect() function even though it needs a little more work.
Because I choose BindIoCompletionCallback(), there is a pool of threads and Windows will pick a thread to inform IO completion to me by calling my own OnIOCompletion(). As long as we safely take care of multi-threads-situation for the functions called in OnIOCompletion(), the client will work just fine. In my client program, each client instance has its own receive buffer so there is nothing I need to do for ordering or blocking something. But, if you are going to make a memory pool shared by all clients, you better synchronize all codes accessing the memory pool in IO completion functions. The same rule applies to anything which can be accessed simultaneously in multi threads.
Let’s move on to the echo server.
This one is a little more complicated because I intentionally use QueueUserWorkItem() function to use the thread pool for the other works as well as the IO completion handling. As you can see, there is a queue and I can queue my work by calling QueueUserWorkItem() function and Windows will pop the top work and do the work (it’s a just function) in one of threads from the pool. So, what are my works?
The first one is creating client sockets beforehand to reduce the time for clients to connect to the server. This can be achieved by using AcceptEx(), another Microsoft-specific extension to the Windows Sockets specification like ConnectEx(). There are the other works like managing a list of clients and echoing what clients send. All those works could be done inside of IO completion functions (OnRecv(), OnAccept(), ..) but I think it’s better to do those works in different threads so that we can quickly post another IO request to a socket. Ideally, this will maximize muti-core power by doing IO work (sending & receiving packets) and our work ( game logic ) concurrently. However, it could hurt overall performance because of crazy context switching. Yes, we need to do profiling.
I don’t really know the behind scenes of the old Thread Pool system. In other words, I have no idea how many threads it creates or how often it checks the work queue. The performance could be worse than your own thread pool system and you might think deleting all thread related functions is good but not practical yet. Wait! you remember it’s “the old” Thread Pool system? That’s why I’m not interested in investigating how it works at all. However, there is the new system. MSDN says “The new thread pool API provides more flexibility and control than the original thread pool API.” We will see.
Lastly, you can find the source code in my public svn server. http://svn.youngwriting.net/public/IOCP – OldThreadPool
(Please wait until I set a public repository for all my open source projects.)
You can find the source code in my Github source repository. https://github.com/young2code/IOCP ( [NewThreadPool] is not complete yet. Please check [OldThreadPool] first. I will update a new post when NewThreadPool is done. It should be soon. )
You need Microsoft Visual C++ 2008 and boost lib to compile the code. Oh, did I mention it supports IPv6 as well? 🙂