Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using ZeroMQ together with Boost::ASIO

Tags:

I've got a C++ application that is using ZeroMQ for some messaging. But it also has to provide a SGCI connection for an AJAX / Comet based web service.

For this I need a normal TCP socket. I could do that by normal Posix sockets, but to stay cross platform portable and make my life easier (I hope...) I was thinking of using Boost::ASIO.

But now I have the clash of ZMQ wanting to use it's own zmq_poll() and ASIO it's io_service.run()...

Is there a way to get ASIO to work together with the 0MQ zmq_poll()?

Or is there an other recommended way to achieve such a setup?

Note: I could solve that by using multiple threads - but it's only a little single core / CPU box that'll run that program with a very low amount of SCGI traffic, so multithreading would be a waste of resources...

like image 656
Chris Avatar asked Oct 10 '12 21:10

Chris


People also ask

Does boost ASIO use threads?

If the run() method is called on an object of type boost::asio::io_service, the associated handlers are invoked on the same thread. By using multiple threads, an application can call multiple run() methods simultaneously.

How does boost ASIO work?

At its core, Boost Asio provides a task execution framework that you can use to perform operations of any kind. You create your tasks as function objects and post them to a task queue maintained by Boost Asio. You enlist one or more threads to pick these tasks (function objects) and invoke them.


2 Answers

After reading the documentation here and here, specifically this paragraph

ZMQ_FD: Retrieve file descriptor associated with the socket The ZMQ_FD option shall retrieve the file descriptor associated with the specified socket. The returned file descriptor can be used to integrate the socket into an existing event loop; the ØMQ library shall signal any pending events on the socket in an edge-triggered fashion by making the file descriptor become ready for reading.

I think you can use null_buffers for every zmq_pollitem_t and defer the event loop to an io_service, completely bypassing zmq_poll() altogether. There appear to be some caveats in the aforementioned documentation however, notably

The ability to read from the returned file descriptor does not necessarily indicate that messages are available to be read from, or can be written to, the underlying socket; applications must retrieve the actual event state with a subsequent retrieval of the ZMQ_EVENTS option.

So when the handler for one of your zmq sockets is fired, you'll have to do a little more work before handling the event I think. Uncompiled pseudo-code is below

const int fd = getZmqDescriptorSomehow(); boost::asio::posix::stream_descriptor socket( _io_service, fd ); socket->async_read_some(     boost::asio::null_buffers(),     [=](const boost::system::error_code& error)     {        if (!error) {            // handle data ready to be read        }      } ); 

note you don't have to use a lambda here, boost::bind to a member function would be sufficient.

like image 134
Sam Miller Avatar answered Sep 30 '22 13:09

Sam Miller


In the end I figured out there are two possible solutions:

  • Sam Miller's where we use the event loop of ASIO
  • The ZeroMQ's event loop by getting the ASIO file descriptors though the .native() methods of the acceptor and the socket and inserting them into the array of zmq_pollitem_t

I have accepted the answer of Sam Miller as that's for me the best solution in SCGI case where constantly new connections are created and ended. Handling the thus every changing zmq_pollitem_t array is big hassle that can be avoided by using the ASIO event loop.

like image 28
Chris Avatar answered Sep 30 '22 13:09

Chris