views:

445

answers:

1

I'm using a MFC CAsyncSocket to do some network communication in a multi-threaded environment. After several hours of trying to get the accepted sockets to accept incoming data I came across a page that states that for a CAsyncSocket's OnReceive function to be called the socket has to be in the context of the main GUI thread. Moving it to the main GUI thread has allowed the socket to start receiving data.

My question is this: does anyone know of a workaround for this? The socket was on a GUI thread before and the OnAccept was being called fine. The accepted socket could be used to send data no problem, just couldn't receive anything. I'd rather not have to re-architect this part of the software if I can avoid it.

+3  A: 

Would it be simpler to simply create a separate thread with its own message queue for your sockets? I don't think CAsyncSocket needs to be created on the main message queue, just some message queue. See the documentation for CWinThread to see how to create a separate thread with its own MFC-compatible message queue.

Unfortunately, it is crucial that you call all socket operations from the context of the new thread. MFC uses global state in hidden classes that use thread-local storage to hold per-thread information, and that information is used in many of the CAsynchSocket's methods. That means CAsynchSocket has thread affinity and you must always use and create it in whatever thread is to be its message pump.

One approach would be to create a CWinThread, create your own custom MFC hidden window on that thread (by creating the window in the context of that thread), and create messages and message handlers on that window for all of the socket operations (creating, connecting, etc) you do. Assure that the thread is pumping messages (the "Run()" method does this) and then post/send messages to your window to control your socket(s).

Also, remember that callbacks from your socket will come in on a separate thread than your UI or worker threads. You'll need to worry about race conditions and possibly GUI thread affinity issues if you're updating GUI objects.

If you're worried about the design impact, just create your own CThreadSafeAsynchSocket proxy object and delegate to the real implementation through message-passing to your hidden window. You can use SendMessage for blocking operations and PostMessage for asynchronous operations. If you wrap the constructor in a factory object, you can delay creation of the socket thread until its needed.

The last concern I can think of is that you'll need to detect when all your proxies have gone away and shut down the thread. You can use a global reference count managed by the CThreadSafeAsynchSocket's constructors/destructors to detect when to shut down the thread. Failing to shut down the thread will keep your app hanging about with a hidden window even after you close your main app window.

David Gladfelter
My original code used a thread with a message pump (tested and working with sending over the socket) but still didn't allow for the OnReceive to be called.
Noaki
Try a full-blown GUI thread with a hidden window.
Aidan Ryan