Professional Documents
Culture Documents
Both ROUTER and DEALER ZeroMQ sockets are designed to work asynchronously. Accordingly to the
current design requirements, we can use one or the other as server. Here we see the case where the ROUTER
sockets are all servers, in the next post we'll see how to write a simple 0MQ application where the DEALER
sockets play in the server role.
Same background of the previous posts, the reference platform is Windows, Visual C++2010, C++, ØMQ 2.2
through the standard ZeroMQ C++ wrapper, plus my zmq::Socket class that you can see on github. There is
nothing complicated in the code, so I guess it could be easily refactored to work on any environment supported
by 0MQ with a limited effort.
The application is designed to to such a pointless job: a few DEALER clients create one message for each
ROUTER available, and then send them all. Then each of them gets a bunch of reply on the socket, and print
them to let the user see the result. (Not) surprisingly, each client gets back all and only the messages that it sent
to the servers.
You can play around changing the number of dealer and routers. In my example I can have just one or two
routers, but you can have how many dealer as you want, and your system allows.
The application in developed to run on a single process, and many threads, to keep testing easy, and this is the
code that spawns all the client and server threads:
1 boost::thread_group threads;
02 {
03 dumpId("DEALER startup");
04 zmq::context_t context(1);
05
std::string id =
06
boost::lexical_cast<std::string>(boost::this_thread::get_id());
07 zmq::Socket skDealer(context, ZMQ_DEALER, id); // 1
08 for(int i =0; i < n; ++i)
09 {
10 skDealer.connect(SKA_CLI[i]); // 2
11 dumpId("DEALER CLI on", SKA_CLI[i]);
12 }
13
14 for(int i =0; i < n; ++i)
15 {
22 {
23 std::string in = skDealer.recvAsString();
24 dumpId(in.c_str());
25 }
26 }
1. For testing purpose, I set each dealer id to its thread id, so that the output looks easier to be checked.
2. The parameter "n" passed to the function represents the number of routers to which each dealer could
connect. Here we are actually connecting to each of them.
3. The parameter "n" represents also the number of messages that each dealer sends, being that one for each
available router. The message sent is build in a bit cryptic way, its length is increased at each iteration, starting
with one, and it contains a repetition of the same character, based on the loop iteration and the "index"
parameter passed to the function to identify the current dealer. The sense of this messy setup is making the
output recognizable during the testing.
4. Here we see the dealer's asynchronicity. Firstly we send all the messages, than we receive them all.
The dealer is sending out a multipart message composed by two frames: its address, that I explicitly set to the
thread id in (1), and the payload. The address will be used by the router to identify which dealer has to get a
reply, and will be consumed by 0MQ. That's way in (4) we should receive just the payload.
Each server runs this code on its own thread:
01 void router4dealer(int nMsg, int id)
02 {
03 dumpId("ROUTER startup");
04 zmq::context_t context(1);
05
06 zmq::Socket skRouter(context, ZMQ_ROUTER);
07 skRouter.bind(SKA_SRV[id]); // 1
08 dumpId("ROUTER SRV on", SKA_SRV[id]);
09
10 std::vector<zmq::Frames> messages;
11 messages.reserve(nMsg); // 2
12
13 for(int i =0; i < nMsg; ++i)
14 {
15 zmq::Frames message = skRouter.blockingRecv(2); // 3
16 dumpId(message[0].c_str(), message[1].c_str());
17 messages.push_back(message);
18 }
19 dumpId("all received");
20
std::for_each(messages.begin(), messages.end(), [&skRouter]
21
(zmq::Frames& frames) // 4
22 {
23 skRouter.send(frames);
24 });
25
26 dumpId("ROUTER done");
27 }
1. The id passed to the function is used to select the right socket address for the current router. You should
carefully check it to avoid out of range values, that should drive this puny application to a miserable crash.
2. We already know how many messages this router is going to receive, so we reserve enough room for them
in the vector where we are going to temporary store them.
3. Only one message is expected from each dealer in the application, and each of them is composed by two
frames, the dealer address, and the message payload.
4. The routers are asynchronous too, and we see that at work here. Firstly we have received all the messages
from the dealers, now we are sending them back. I am using an STL for_each algorithm that iterates on each
element of the vector where the messages have been stored. For each message, a C++11 lambda function is
called that makes use of the socket, accessed by reference, and accepts in input the current element of the
vector, again by reference. In the lambda body we just send the message back through the socket.