Ask and Ye Shall Receive

So let’s start with some code. We’ll begin, of course, with a “Hello World” example. We’ll make a client and a server. The client sends “Hello” to the server, which replies with “World” (Figure 1-1). Example 1-1 presents the code for the server in C, which opens a ØMQ socket on port 5555, reads requests on it, and replies with “World” to each request.

Example 1-1. Hello World server (hwserver.c)

//
//  Hello World server
//  Binds REP socket to tcp://*:5555
//  Expects "Hello" from client, replies with "World"
//
#include <zmq.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main (void)
{
    void *context = zmq_ctx_new ();

    //  Socket to talk to clients
    void *responder = zmq_socket (context, ZMQ_REP);
    zmq_bind (responder, "tcp://*:5555");

    while (1) {
        //  Wait for next request from client
        zmq_msg_t request;
        zmq_msg_init (&request);
        zmq_msg_recv (&request, responder, 0);
        printf ("Received Hello\n");
        zmq_msg_close (&request);

        //  Do some 'work'
        sleep (1);

        //  Send reply back to client
        zmq_msg_t reply;
        zmq_msg_init_size (&reply, 5);
        memcpy (zmq_msg_data (&reply), "World", 5);
        zmq_msg_send (&reply, responder, 0);
        zmq_msg_close (&reply);
    }
    //  We never get here but if we did, this would be how we end
    zmq_close (responder);
    zmq_ctx_destroy (context);
    return 0;
}
Request-reply

Figure 1-1. Request-reply

The REQ-REP socket pair is in lockstep. The client issues zmq_msg_send() and then zmq_msg_recv(), in a loop (or once if that’s all it needs). Any other sequence (e.g., sending two messages in a row) will result in a return code of -1 from the send or recv call. Similarly, the server issues zmq_msg_recv() and then zmq_msg_send(), in that order, as often as it needs to.

ØMQ uses C as its reference language, and this is the main language we’ll use for examples. If you’re reading this online, the link below the example takes you to translations into other programming languages. For print readers, Example 1-2 shows what the same server looks like in C++.

Example 1-2. Hello World server (hwserver.cpp)

//
//  Hello World server in C++
//  Binds REP socket to tcp://*:5555
//  Expects "Hello" from client, replies with "World"
//
#include <zmq.hpp>
#include <string>
#include <iostream>
#include <unistd.h>

int main () {
    //  Prepare our context and socket
    zmq::context_t context (1);
    zmq::socket_t socket (context, ZMQ_REP);
    socket.bind ("tcp://*:5555");

    while (true) {
        zmq::message_t request;

        //  Wait for next request from client
        socket.recv (&request);
        std::cout << "Received Hello" << std::endl;

        //  Do some 'work'
        sleep (1);

        //  Send reply back to client
        zmq::message_t reply (5);
        memcpy ((void *) reply.data (), "World", 5);
        socket.send (reply);
    }
    return 0;
}

You can see that the ØMQ API is similar in C and C++. In a language like PHP, we can hide even more and the code becomes even easier to read, as shown in Example 1-3.

Example 1-3. Hello World server (hwserver.php)

<?php
/*
 *  Hello World server
 *  Binds REP socket to tcp://*:5555
 *  Expects "Hello" from client, replies with "World"
 * @author Ian Barber <ian(dot)barber(at)gmail(dot)com>
 */

$context = new ZMQContext(1);

//  Socket to talk to clients
$responder = new ZMQSocket($context, ZMQ::SOCKET_REP);
$responder->bind("tcp://*:5555");

while (true) {
    //  Wait for next request from client
    $request = $responder->recv();
    printf ("Received request: [%s]\n", $request);

    //  Do some 'work'
    sleep (1);

    //  Send reply back to client
    $responder->send("World");
}

Example 1-4 shows the client code.

Example 1-4. Hello World client (hwclient.c)

//
//  Hello World client
//  Connects REQ socket to tcp://localhost:5555
//  Sends "Hello" to server, expects "World" back
//
#include <zmq.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>

int main (void)
{
    void *context = zmq_ctx_new ();

    //  Socket to talk to server
    printf ("Connecting to hello world server...\n");
    void *requester = zmq_socket (context, ZMQ_REQ);
    zmq_connect (requester, "tcp://localhost:5555");

//     int request_nbr;
//     for (request_nbr = 0; request_nbr != 10; request_nbr++) {
//         zmq_msg_t request;
//         zmq_msg_init_size (&request, 5);
//         memcpy (zmq_msg_data (&request), "Hello", 5);
//         printf ("Sending Hello %d...\n", request_nbr);
//         zmq_msg_send (&request, requester, 0);
//         zmq_msg_close (&request);
// 
//         zmq_msg_t reply;
//         zmq_msg_init (&reply);
//         zmq_msg_recv (&reply, requester, 0);
//         printf ("Received World %d\n", request_nbr);
//         zmq_msg_close (&reply);
//     }
    sleep (2);
    zmq_close (requester);
    zmq_ctx_destroy (context);
    return 0;
}

Now this looks too simple to be realistic, but a ØMQ socket is what you get when you take a normal TCP socket, inject it with a mix of radioactive isotopes stolen from a secret Soviet atomic research project, bombard it with 1950s-era cosmic rays, and put it into the hands of a drug-addled comic book author with a badly disguised fetish for bulging muscles clad in spandex (Figure 1-2). Yes, ØMQ sockets are the world-saving superheroes of the networking world.

There was a terrible accident...

Figure 1-2. There was a terrible accident...

You could throw thousands of clients at this server, all at once, and it would continue to work happily and quickly. For fun, try starting the client and then starting the server, see how it all still works, and then think for a second what this means.

Let us explain briefly what these two programs are actually doing. They create a ØMQ context to work with, and a socket. Don’t worry what the words mean. You’ll pick it up. The server binds its REP (reply) socket to port 5555. It then waits for a request in a loop, and responds each time with a reply. The client sends a request and reads the reply back from the server.

If you kill the server (Ctrl-C) and restart it, the client won’t recover properly. Recovering from crashing processes isn’t quite that easy. Making a reliable request-reply flow is complex enough that we won’t cover it until “Reliable Request-Reply Patterns” in Chapter 4.

There is a lot happening behind the scenes, but what matters to us programmers is how short and sweet the code is and how often it doesn’t crash, even under a heavy load. This is the request-reply pattern, probably the simplest way to use ØMQ. It maps to RPC (remote procedure calls) and the classic client/server model.

Get ZeroMQ now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.