1

I have a current implementation of ZMQ in my program that works great. I am initializing my monitor with ZMQ_EVENT_CONNECTED then checking for the event with the ZMQ provided bool check_event(10) - 10 is the timeout length.

I want to change my monitor initialization to ZMQ_EVENT_ALL and handle each event differently. Say my socket randomly disconnects, I want to listen for that specific event and display an error to my user. I don't want the user to still attempt sending a message and be caught in limbo - which is the case now because I am only listening for the CONNECTED event and not looking for or handling other events.

How can I handle each specific event differently if check_event() is only a bool? Is there something other than check_event() that looks for an event? Maybe one that will return the events value for me to check against ? I am open to suggestions

My connect function is here.

void control::connect(std::string ipAddr) {

    zmq::context_t context (1);                             // Creating context for connection. 
    zmq::socket_t socket(context, zmq::socket_type::req);   // Setting socket with REQUEST type. Table is a REPLY type. 
    socket.set(zmq::sockopt::routing_id, "rtc");            // Setting the routing ID for the socket
     
     // Monitor to check for socket being connected to table.
    monitor.init(socket, "inproc://monitor", ZMQ_EVENT_CONNECTED);  // Initialize the connection monitor.

    int connectionAttempt = 0;                              // Holds number of connection attempts

    // While the program is not connected to the rate table, attempt connection. 
    while(!isConnected){
        try{
            cout << "connecting...\n";                      // Notify user of attempt to connect
            connectionAttempt++;                            // increment attempt count
            socket.connect(ipAddr);                         // Connect to the ip address
            
            if(monitor.check_event(10)){                    // Wait 10ms - *IF* the CONNECT event kicks
                isConnected = true;                         // Set the connection bool
                continue;                                   // continue past the while loop
            }

        }
        catch(zmq::error_t err){                            // Catch any error
            cout << "ERROR: " << err.what() << endl;        // Output error
        }
        sleep(2);                                           // Sleep for 2s if not connected - then re-try

        // 2s * 15 iterations - If not connected within 30 seconds. Throw error, exit.
        if(connectionAttempt == 15){
            // TODO - turn this into an actual error. 
            cout << "\n\nERROR: Max Connection Attempts Exceded.\nPlease check cables and verify rate table is turned on.\nThen restart application.\n";
            exit(3);
        }
    }

    // NOTIFY OF APP COMMANDS
    printf(" TYPE: 'HELP' for a list of available commands.\n");
    printf(" TYPE: 'QUIT' to close the program.\n\n");

    // DO-While loop to allow user to send commands. 
    do{
         // Once we are connected, we want to listen for a disconenct event.
         if(***CHECK FOR DISCONNECT HERE***){ isConnected = false; continue;}

        sendCommand(socket, context);
    }while(isConnected == true);

    // Perform closing procedure if we get to this point. 
    closeConnection(ipAddr, socket, context);
}

SOLUTION:

This is the solution I came up with, I set an eventID variable with the events in from ZMQ. Then in my code I check the set eventID value.

class socketMonitor : public zmq::monitor_t {
public:
    // listening for the on_event_connected event, notify user if successful. 
    void on_event_connected(const zmq_event_t& event, const char* addr) override {
        eventID = ZMQ_EVENT_CONNECTED;
        eventName = "Connected";
    }

    void on_event_disconnected(const zmq_event_t& event, const char* addr) override {
        eventID = ZMQ_EVENT_DISCONNECTED;
        eventName = "Disconnected";
    }

    void on_event_connect_retried(const zmq_event_t& event, const char* addr) override {
        eventID = ZMQ_EVENT_CONNECT_RETRIED;
        eventName = "Connection Retired";
    }

    void on_event_listening(const zmq_event_t& event, const char* addr) override {
        eventID = ZMQ_EVENT_LISTENING;
        eventName = "Listening";
    }

    void on_event_connect_delayed(const zmq_event_t& event, const char* addr) override {
        eventID = ZMQ_EVENT_CONNECT_DELAYED;
        eventName = "Connect Delayed";
    }

    void on_event_accept_failed(const zmq_event_t& event, const char* addr) override {
        eventID = ZMQ_EVENT_ACCEPT_FAILED;
        eventName = "Accept Failed";
    }

    void on_event_closed(const zmq_event_t& event, const char* addr) override {
        eventID = ZMQ_EVENT_CLOSED;
        eventName = "Closed";
    }

    void on_event_bind_failed(const zmq_event_t& event, const char* addr) override {
        eventID = ZMQ_EVENT_BIND_FAILED;
        eventName = "Bind Failed";
    }

    int eventID;
    std::string eventName;
};

and I then check it like this in my code...

    socketMonitor monitor;
    monitor.check_event(1);
    if(monitor.eventID == ZMQ_EVENT_DISCONNECTED){
        isConnected == false;       // Set bool to false
        monitor.eventID = 0;        // Reset ID
    }

So far this seems to be doing what I need it to do.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Chip Brommer
  • 103
  • 9

2 Answers2

0

After roaming around in the zmq.hpp file for a while I found a possible solution. You can inherit from zmq::monitor_t and then override the different on_event_* functions. This will still require you to call check_event, alternatively, you can use the monitor function, but you will require to do that in a separate thread as it is blocking.

So you can do something like this:

class MyMonitor : public zmq::monitor_t {
    void on_event_connected(const zmq_event_t &event, const char *addr) override {
        std::cout << " -- Connected to " << addr << std::endl;
    }
};

And then somewhere in your main thread do:

MyMonitor monitor;
monitor.init(socket, "inproc://monitor", ZMQ_EVENT_ALL);
socket.connect(ipAddr);
monitor.check_event(10);

The function check_event will trigger a call to on_event_connected. For looking up the other on_event_* functions, just take a look in zmq.hpp file or utilise your IntelliSense if available.

I hope this was helpful, have fun!

Quizzarex
  • 167
  • 2
  • 12
  • 1
    If this was helpful, please up-vote and accept it as an answer. – Quizzarex May 05 '21 at 06:26
  • The problem with this implementation is that "check_event" checks all events - and will return true upon any event kicking. So even if the disconnect event triggers, I wont have anything to check against. - as you can see in my code, I already have a custom override for monitor_t called monitor. - I believe I have found a solution though. – Chip Brommer May 05 '21 at 17:39
  • I believe it is good practice to share your own solution now that you found one. Secondly, I think you completely misunderstood the point here: The point is that you make your own monitor class that implements the events that you want to listen to, thus, you listen to all events and only the implemented ones will be triggered essentially. Thirdly, what you describe as a custom override for monitor_t, I believe is just an instance of monitor_t. At least it is not really clear from your example as it is to say the least, deficient. – Quizzarex May 06 '21 at 05:32
  • Im a little delayed on a response. But let me attempt to explain here. When you initialize with ```ZMQ_EVENT_ALL``` and then you call ```check_event(1)``` its not only checking the functions you created - it checks against *ALL* the monitor events - even if you do not have a function created for it. For example - if you run the code there in your "main thread" the first event that will get returned is ```ZMQ_EVENT_CONNECT_DELAY``` and the check_event bool will return true even though you have created no override function for that event. – Chip Brommer May 11 '21 at 16:45
  • Just call `check_event` function without caring about what it returns and "wait" for your events to trigger in your inherited version of zmq::monitor_t. – Quizzarex May 12 '21 at 07:13
0

This is not the answer you are looking for but after struggling with zmq::monitor_t class in the past I gave up and went back to the core libzmq zmq-socket-monitor

http://api.zeromq.org/4-3:zmq-socket-monitor

The main advantage here is you have access to the monitor output socket and can poll() in threads that are also reading other sockets. You can subscribe to ZMQ_EVENT_ALL and have full control over what you do with each type of event.

BTW I like cppzmq and use it for the rest of my code, I just dont use the monitor as I find it very restrictive with its inheritance interface.

James Harvey
  • 912
  • 4
  • 6