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
;
}
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.
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.