简体   繁体   中英

How can I send a ZeroMQ message from a ROUTER socket to a specific DEALER socket using cppzmq?

I've put together this minimal example in order to send a message from a Router socket to a specific DEALER socker (That has it's identity set). When running these two programs it appears to hang on the ROUTER waiting from the reply from the DEALER , and the DEALER hangs waiting for the request from the ROUTER . So it appears that the message that the ROUTER is sending is never making it to the DEALER .

Router.cpp

#include <iostream>
#include <zmq.hpp>
#include <string>
#include <thread>
#include <chrono>

int main() {
    zmq::context_t context;
    zmq::socket_t socket (context, zmq::socket_type::router);
    // Enforce sending routable messages only
    socket.setsockopt(ZMQ_ROUTER_MANDATORY, 1);
    socket.bind("tcp://*:5555");

    try {
        std::string jobRequest = "ExampleJobRequest";

        std::cout << "Router: Sending msg: " << jobRequest << std::endl;

        // Set the address, then the empty delimiter and then the request itself
        socket.send("PEER2", ZMQ_SNDMORE);
        //socket.send(zmq::message_t(), ZMQ_SNDMORE);
        socket.send(zmq::str_buffer("ExampleJobRequest")) ;

        // Set the address, then the empty delimiter and then the request itself
        socket.send("PEER2", ZMQ_SNDMORE);
        //socket.send(zmq::message_t(), ZMQ_SNDMORE);
        socket.send(zmq::str_buffer("ExampleJobRequest")) ;

        // Receive the reply from the camera
        std::cout << "Router: Waiting for reply from camera " << std::endl;
        zmq::message_t reply;
        socket.recv(&reply);

        std::cout << "Router: Received " <<  std::string(static_cast<char*>(reply.data()), reply.size()) << std::endl;
    } catch (std::exception e) {
        std::cout << "Router Error: " << e.what();
    }

    std::this_thread::sleep_for(std::chrono::seconds(1));
    socket.close();
    context.close();
}

Dealer.cpp

#include <zmq.hpp>
#include <string>
#include <iostream>
#include <thread>

int main (void)
{
    //  Prepare our context and socket
    zmq::context_t context;
    zmq::socket_t socket (context, zmq::socket_type::dealer);

    std::cout << "Dealer: Connecting to RunJob server… \n";
    socket.setsockopt(ZMQ_IDENTITY, "PEER2", 5);
    socket.connect ("tcp://localhost:5555");

    while(true) {
        try {
            // Wait for next request from client
            std::cout << "Dealer: Waiting for request" << std::endl;
            zmq::message_t request;
            zmq::message_t empty;

            // Receive request
            socket.recv(&request);

            std::string requestString = std::string(static_cast<char*>(request.data()), request.size());

            std::cout << "Dealer: Received request" << std::endl;
            std::cout << requestString << std::endl;

            // ZMQ_SNDMORE - "Specifies that the message being sent is a multi-part message, and that further message parts are to follow"
            socket.send(zmq::str_buffer("Job completed"), zmq::send_flags::dontwait);
        }catch (std::exception e) {
            std::cout << "Router Error: " << e.what();
        }
    }

    // Used to set various 0MQ Socket Settings
    // ZMQ_Linger - Set linger period for socket shutdown
    socket.setsockopt(ZMQ_LINGER, 0);
    socket.close();
    context.close();

    return 0;
}

I had originally considered that I should be prepending the message with an empty delimiter, socket.send(zmq::message_t(), ZMQ_SNDMORE); , but this caused an error. Also using the following also caused an error to be thrown in the try/catch block. The error simply prints 'Unknown error':

zmq::message_t delimiter(0);
socket.send(delimiter, ZMQ_SNDMORE);

Using the following to create the delimiter also causes the same error:

socket.send(zmq::message_t(), ZMQ_SNDMORE);

To my knowledge, when using cppzmq you don't need to add the empty delimiter (I could be wrong about this, but after reading around and looking at other peoples example and testing my own code, this is what i determined).

Here's a very basic diagram with the end goal of this design :

路由器到经销商的消息传递设计

In my research, i haven't found a good example of this code. The Cppzmq github has very little documentation and few examples.

Here are some other sources i've looked at:

The main idea about ROUTER/DEALER pattern is that it is an asynchronous generalisation of REPLY/REQUEST. Yet you are trying to reverse the sockets in your pattern, discovering it doesn't fit and contorting the code to try and make it fit. Don't do that.

What you need to do is "go with the flow". In the simple method, for which examples exist, the DEALER should send the first message. The ROUTER then responds to that.

The next level is for the DEALER to identify itself in its startup message. The ROUTER can then give a specific response to that DEALER.

At the next level you can go truly asynchronous. The ROUTER can take a copy of each DEALER's identification message, and use the message copies to send asynchronous messages to any DEALER at any time. One copy of the identification message would have the "PEER2" frame appended to it and sent to the DEALER. This works because the copies of the messages include the routing frames. Ideally, you would also strip the 'message' frames, to leave only the routing frames in the copy.

Caveat - I don't use cppzmq, I use CZMQ. I can say that using CZMQ this sort of frame manipulation is very easy.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM