I am trying to integrate ZMQ into an existing windows application that relies heavily on MFC sockets (CASyncSocket).
I've got a CWinThread derived UI thread (without a GUI) that communicates with a server asynchronously using CAsyncSocket. I would like to add a ZMQ inproc communication line to handle communicating the data received from the server (on a REQ/REP basis) to other threads within the application.
Using CAsyncSocket, the OnReceive method is called by the MFC framework whenever new data is available on the socket to be received (that might be an over-simplification to the hardcore MFC gurus out there).
Is there any such mechanism in ZMQ? Or do I have to add an additional dedicated WorkerThread that the UI thread launches to handle my ZMQ communications to the rest of the app? The traffic on both pipelines is minimal so I really don't want to have to create 2 separate threads if I can get by with 1.
Note, I've got the basics working, I'm just having problems with synchronization. If I use blocking recv/send with ZMQ, it starves out my CAsycSocket because the windows messages never get processed by the thread resulting in sometimes never getting the data from the server that the ZMQ is supposed to be delivering. But if I use non-blocking ZMQ calls, then the thread frequently ends up sitting idle because it doesn't know to read off the ZMQ socket.
Ultimately, the answer is no. There are no current callback/notifications for when data arrives in ZeroMQ that you can link into. I was also unable to find any fork that adds this functionality.
I was unable to get ZMQ working while using the traditional OnReceive calls provide by the MFC socket framework within a single thread and adding a 2nd thread to dedicate to ZMQ defeated the whole purpose for using it (it is being used for thread synchronization).
My implementation that works ended up dropping MFC sockets and using ZMQ for both my inproc server (for communicating with other thread) as well as my TCP (non-ZMQ) server connection and using a blocking polling call (zmq_poll()) in the OnIdle() method (returning 1 every time to create a busy loop). The blocking poll
Note: This answer requires using ZMQ 4 or later since earlier versions of ZMQ will not communicate with a regular TCP socket connection.
You can signal from thread to thread with custom Windows messages. Here is a custom message:
To send it to a thread that has a window use PostMessage or SendMessage to the HWND. Add it to the window's message map with ON_MESSAGE.
To send it to a CWinThread-derived thread that has no windows use PostThreadMessage and receive it with ON_THREAD_MESSAGE.
You could call
zmq_recv()
with theZMQ_NOBLOCK
flag inside your app's main message loop by overriding yourCWinApp::OnIdle()
.zmq_recv
will return immediately if there is no data waiting on the socket. If there's data, handle it - but be aware that if you do something slow, you'll make the app unresponsive.Edit: I didn't realize that
OnIdle
only gets called once when the message queue becomes empty. But according to MSDN's documentation, you can return a nonzero value to keep getting called forever:OnIdle
and supplies0
as thelCount
argument.OnIdle
performs some processing and returns a nonzero value to indicate it should be called again to do further processing.OnIdle
again, incrementing thelCount
argument.OnIdle
finishes processing all its idle tasks and returns0
. This tells the message loop to stop callingOnIdle
until the next message is received from the message queue, at which point the idle cycle restarts with the argument set to0
.I also found this thread on GameDev.net where a user said:
So, at least according to one person, it's a common technique.