ZeroMQ’s RADIO socket type, when configured for UDP, is an unreliable datagram transport that prioritizes message delivery speed and simplicity over guaranteed delivery and ordering.

Let’s see it in action. Imagine a simple scenario: a fanout publisher sending identical messages to multiple subscribers.

Publisher (radio_pub.py):

import zmq
import time

context = zmq.Context()
socket = context.socket(zmq.RADIO)
socket.bind("udp://239.0.0.1:5555") # Multicast address

print("Publisher started, sending messages...")

for i in range(100):
    message = f"Message {i}"
    socket.send_string(message)
    print(f"Sent: {message}")
    time.sleep(0.1)

print("Publisher finished.")

Subscriber (dish_sub.py):

import zmq

context = zmq.Context()
socket = context.socket(zmq.DISH)
socket.connect("udp://239.0.0.1:5555") # Multicast address

print("Subscriber started, listening for messages...")

while True:
    try:
        message = socket.recv_string()
        print(f"Received: {message}")
    except zmq.ZMQError as e:
        if e.errno == zmq.ETERM:
            break
        print(f"Error receiving message: {e}")

print("Subscriber finished.")

When you run radio_pub.py and then one or more instances of dish_sub.py, you’ll observe that the subscribers receive messages, but not all of them might get every single message, especially if network conditions are poor or if there are many subscribers.

The RADIO socket type is designed for scenarios where you have a single sender broadcasting data to potentially many receivers, and some message loss is acceptable. Think of it like a radio broadcast: the station sends out a signal, and anyone with a radio can tune in and receive it. The station doesn’t know who is listening, nor does it care if a listener misses a word. ZeroMQ’s RADIO socket embodies this one-to-many, fire-and-forget philosophy.

Internally, RADIO uses UDP. UDP is a connectionless protocol, meaning there’s no handshake to establish a persistent connection. When you send_string on a RADIO socket, ZeroMQ essentially packages your message into a UDP datagram and sends it to the specified address and port. There’s no acknowledgment from the receiver, no retransmission mechanism built into the protocol itself. This makes it incredibly fast and lightweight.

The DISH socket is the counterpart to RADIO. It’s designed to receive messages sent by RADIO sockets. When a DISH socket connects to a RADIO socket’s address, it starts listening for UDP datagrams. Crucially, DISH is also an unreliable datagram socket. It receives what it can, as it can. It doesn’t ask the RADIO socket to resend anything it missed.

The primary advantage of RADIO/DISH is its scalability for fanout. A single RADIO socket can send to a vast number of DISH sockets without the sender being aware of, or burdened by, the number of receivers. The UDP multicast capabilities further enhance this, allowing a single packet to be delivered to multiple recipients on a network segment simultaneously. The RADIO socket simply sends the packet once; the network infrastructure handles the duplication to multiple subscribers.

The RADIO socket doesn’t maintain any state about its subscribers. It doesn’t track which messages have been received or lost. When you send_string, the message is handed off to the underlying UDP stack. If the network drops the packet, that’s it. The RADIO socket is already onto sending the next message. This is why it’s "unreliable" – delivery is not guaranteed.

The DISH socket, on the other hand, is designed to receive these datagrams. It will attempt to receive every datagram that arrives at its listening address and port. However, if datagrams arrive faster than the application can process them, or if network congestion causes packet loss, the DISH socket itself doesn’t have a built-in buffer to guarantee that every single one is eventually made available to the application. It receives what it can from the OS’s UDP buffer.

The bind and connect operations for RADIO and DISH sockets typically use UDP addresses, often including multicast addresses (like udp://239.0.0.1:5555). Multicast is a natural fit for the RADIO/DISH pattern, as it allows a single sender to efficiently send to many receivers without the sender needing to know their individual IP addresses. The RADIO socket joins the multicast group, and DISH sockets also join that same group to receive the broadcasts.

The specific behavior regarding message ordering and loss is entirely dependent on the underlying UDP transport and network conditions. ZeroMQ doesn’t add any layers to enforce ordering or guarantee delivery for RADIO/DISH. You get the raw speed of UDP with a simple API.

If you need reliable delivery, you’d typically use other ZeroMQ socket patterns like REQ/REP or PUB/SUB with TCP, or implement your own reliability mechanisms on top of RADIO/DISH.

The next challenge you’ll encounter is managing the potential for message loss and out-of-order delivery when using RADIO/DISH in a real-world application.

Want structured learning?

Take the full Zeromq course →