ZeroMQ

Language: CPP

Networking

ZeroMQ (also known as ØMQ) was created in 2007 by iMatix Corporation to provide a lightweight messaging layer that is faster and more flexible than traditional message brokers like AMQP or JMS. Unlike typical brokers, ZeroMQ is brokerless—applications communicate directly via sockets while still supporting rich messaging patterns. It has become popular in finance, IoT, distributed systems, and high-performance computing.

ZeroMQ is a high-performance asynchronous messaging library for building distributed and concurrent applications. It provides sockets that carry atomic messages across processes, machines, or threads with patterns like pub-sub, request-reply, and pipeline.

Installation

linux: sudo apt install libzmq3-dev
mac: brew install zeromq
windows: vcpkg install zeromq

Usage

ZeroMQ provides socket types (`REQ`, `REP`, `PUB`, `SUB`, `PUSH`, `PULL`) that can be connected in messaging topologies. It supports asynchronous, non-blocking communication, high throughput, and minimal latency.

Hello World server (reply)

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

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

    while (true) {
        zmq::message_t request;
        socket.recv(request);
        std::cout << "Received: " << request.to_string() << std::endl;

        zmq::message_t reply("World", 5);
        socket.send(reply, zmq::send_flags::none);
    }
    return 0;
}

A simple ZeroMQ server using the request-reply pattern.

Hello World client (request)

zmq::context_t context(1);
zmq::socket_t socket(context, ZMQ_REQ);
socket.connect("tcp://localhost:5555");

zmq::message_t request("Hello", 5);
socket.send(request, zmq::send_flags::none);

zmq::message_t reply;
socket.recv(reply);
std::cout << "Received reply: " << reply.to_string() << std::endl;

A simple client sending requests and receiving replies.

Publish-Subscribe

// Publisher
zmq::socket_t pub(context, ZMQ_PUB);
pub.bind("tcp://*:5556");
zmq::message_t msg("Update 1", 8);
pub.send(msg, zmq::send_flags::none);

// Subscriber
zmq::socket_t sub(context, ZMQ_SUB);
sub.connect("tcp://localhost:5556");
sub.set(zmq::sockopt::subscribe, "");

Implements a pub-sub model where one publisher sends updates to many subscribers.

Pipeline (Push-Pull)

zmq::socket_t push(context, ZMQ_PUSH);
push.bind("tcp://*:5557");
zmq::socket_t pull(context, ZMQ_PULL);
pull.connect("tcp://localhost:5557");

Implements a task distribution model where tasks are pushed by producers and pulled by workers.

Non-blocking receive

zmq::message_t msg;
bool received = socket.recv(msg, zmq::recv_flags::dontwait);
if (received) {
    std::cout << "Got message: " << msg.to_string() << std::endl;
}

Demonstrates asynchronous message receiving without blocking.

Polling multiple sockets

zmq::pollitem_t items[] = {{socket1, 0, ZMQ_POLLIN, 0}, {socket2, 0, ZMQ_POLLIN, 0}};
zmq::poll(items, 2, 1000);

Polls multiple sockets simultaneously with a timeout.

Error Handling

zmq::error_t: Connection refused: Ensure the server socket is bound and listening before connecting clients.
zmq::error_t: Operation would block: Occurs with non-blocking sockets. Check readiness with polling before sending/receiving.
Dropped messages in PUB-SUB: Subscribers only receive messages after they have subscribed. Consider buffering or queueing if needed.

Best Practices

Choose the right messaging pattern (`REQ-REP`, `PUB-SUB`, `PUSH-PULL`) for your use case.

Use non-blocking sockets or polling to avoid deadlocks.

Always close sockets and terminate the context to free resources.

Handle network errors gracefully; ZeroMQ does not guarantee message delivery by default.

For secure messaging, combine with encryption frameworks like CurveZMQ.