Handling asynchronous sockets in WinSock?

Jason Dietrich picture Jason Dietrich · Feb 18, 2011 · Viewed 8.5k times · Source

I'm using a message window and WSAAsyncSelect. How can I keep track of multiple sockets (the clients) with one message window?

Answer

Chris Becke picture Chris Becke · Feb 18, 2011

Windows Supports several modes of socket operation, and you do need to be clear which one you are using:

  • Blocking sockets. send and recv block.
  • Non-Blocking sockets: send and recv return E_WOULDBLOCK, and select() is used to determine which sockets are ready
  • Asynchronous sockets: WSAAsyncSelect - sockets post event notifications to an HWND.
  • EventSockets: WSAEventSelect - sockets signal events.
  • Overlapped Sockets: WSASend and WSARecv are used with sockets by passing in the OVERLAPPED structures. Overlapped Sockets can be combined with IOCompletionPorts and provide the best scalability.

In terms of convenience, asynchronous sockets are simple, and supported by MFC CAsyncSocket class.

Event Sockets are tricky to use as the maximum number of objects passable to WaitForMultipleObjects is 64.

Overlapped sockets, with IO CompletionPorts, is the most scalable way to handle sockets and allows windows based servers to scale to tens of thousands of sockets.


In my experience, when using Async Sockets, the following things come to mind:

  • Handling FD events via window messages can handle "lots" of sockets, but performance will begin to suffer as all the event handling is done in one thread, serialized through a message queue that might be busy handling UI events too if used in a single threaded GUI app.

  • If you are hosting GUI windows or timers on the same thread as lots of sockets: WM_TIMER and WM_PAINT messages are low priority, and will only be generated if the message queue is empty. Very busy sockets can thus cause GUI painting, or SetTimer based timing to fail.

  • Creating a dedicated worker thread to handle your sockets if hosting a GUI solves these problems. Given that the worker thread will have a message loop, you can use the message queue for inter-thread comms - just post WM_APP messages to the thread.

  • The easiest way to map FD callbacks to your socket objects is to create an Array of SocketObjects for each HWND that will be receiving messages, and then use WM_USER+index as the message ID each time you call WASAsyncSelect. Then, when you receive messages in the range WM_USER to WM_USER+(array size) you can quickly extract the corresponding state object. WM_USER is 0x400, and WM_APP is 0x8000, so you can index up to 31744 sockets per message window using this method.

  • Don't use a static scope array. You need to associate the array with the window as you might want to create sockets on multiple threads. Each thread will need its own message loop, and message window.

  • HWND_MESSAGE is your friend