ZBEACON(3)
π zbeacon(3) β CZMQ Manual π
okay so hi bestie π you just opened the zbeacon page out of the CZMQ Manual (section 3, the fun one) and honestly? you're about to have a whole good time. this is the LAN discovery + presence class and it's literally so useful. let's go π¦
β¨ NAME β¨
zbeacon β Class for LAN discovery and presence π
π¦ SYNOPSIS π¦
so it's C, so it's giving pictures and pointers, but stay with me here π«Ά every single move you can make, in order:
πΈ create a new zbeacon actor instance
zactor_t *beacon = zactor_new (zbeacon, NULL);
π destroy the zbeacon instance
zactor_destroy (&beacon);
π£οΈ enable verbose logging of commands and activity
zstr_send (beacon, "VERBOSE");
like, all the chatter. very main character of you.
βοΈ CONFIGURE
this configures the beacon to run on the UDP port you hand it, and it gives you back the name of the host β which you can use as the endpoint for incoming connections. that's the whole point, bestie π
to force the beacon onto a specific interface, set the environment variableZSYS_INTERFACE, or callzsys_set_interface()before you create the beacon. and if the system doesn't support UDP broadcasts at all (like, no workable interface), you get an empty hostname back. not a vibe but it is what it is π
// picture time: 's' = C string, 'i' = int
zsock_send (beacon, "si", "CONFIGURE", port_number);
char *hostname = zstr_recv (beacon);
π‘ PUBLISH
starts broadcasting your beacon on an interval, measured in msec. the beacon data can be at most UDP_FRAME_MAX bytes β that constant is defined in zsys.h and it's 255.
// picture: 'b' = byte * data + size_t size
zsock_send (beacon, "sbi", "PUBLISH", data, size, interval);
π€« SILENCE
stop broadcasting the beacon. so peaceful.
zstr_sendx (beacon, "SILENCE", NULL);
π SUBSCRIBE
start listening to beacons from your peers π«Ά the filter does a prefix match on the beacons coming in, which is how you throw out the junk. and ngl, note this: any received data that's identical to our own broadcast beacon_data gets discarded in any case. also, if the filter size is zero, we get all the peer beacons.
zsock_send (beacon, "sb", "SUBSCRIBE", filter_data, filter_size);
π UNSUBSCRIBE
stop listening to the other peers.
zstr_sendx (beacon, "UNSUBSCRIBE", NULL);
π receive the next beacon from a peer
received beacons are always a 2-frame message: first the ipaddress of the sender, then the binary beacon data exactly as published by the sender. periodt.
zmsg_t *msg = zmsg_recv (beacon);
π§ this is the zbeacon constructor, as a zactor_fn
CZMQ_EXPORT void
zbeacon (zsock_t *pipe, void *unused);
π₯ and the self test of this class
CZMQ_EXPORT void
zbeacon_test (bool verbose);
heads up bestie: Please add '@interface' section in './../src/zbeacon.c'.
π DESCRIPTION π
okay so the zbeacon class implements a peer-to-peer discovery service for local networks, and it's honestly so cute. a beacon can broadcast and/or capture service announcements using UDP messages on the local area network, and this implementation uses IPv4 UDP broadcasts. you get to define the format of your outgoing beacons, and set a filter that validates the incoming ones. and the best part? beacons are sent and received asynchronously in the background. slay. π
this class replaces zbeacon_v2, and it's meant for applications that use the CZMQ v3 API β meaning, zsock.
π EXAMPLE π
okay bestie, this whole section is lifted straight out of the zbeacon_test method, and it is a lot of code. so grab a snack π, we're going in. i'm gonna walk you through it in chunks so it doesn't fry your brain, but every single line stays exactly as written, no cap.
First up β Test 1: two beacons, one speaking, one listening. Iconic. One beacon yaps, one beacon listens, that's the whole vibe.
// Test 1 - two beacons, one speaking, one listening
// Create speaker beacon to broadcast our service
zactor_t *speaker = zactor_new (zbeacon, NULL);
assert (speaker);
if (verbose)
zstr_sendx (speaker, "VERBOSE", NULL);
zsock_send (speaker, "si", "CONFIGURE", 9999);
char *hostname = zstr_recv (speaker);
if (!*hostname) {
printf ("OK (skipping test, no UDP broadcasting)\n");
zactor_destroy (&speaker);
freen (hostname);
return;
}
freen (hostname);
lowkey important: if hostname comes back empty, the test just bails with "OK (skipping test, no UDP broadcasting)" β that's the no-UDP-broadcasting escape hatch, not a bug, bestie.
Now the listener side π
// Create listener beacon on port 9999 to lookup service
zactor_t *listener = zactor_new (zbeacon, NULL);
assert (listener);
if (verbose)
zstr_sendx (listener, "VERBOSE", NULL);
zsock_send (listener, "si", "CONFIGURE", 9999);
hostname = zstr_recv (listener);
assert (*hostname);
freen (hostname);
and then we broadcast the magic value 0xCAFE because why not, it's cute:
// We will broadcast the magic value 0xCAFE
byte announcement [2] = { 0xCA, 0xFE };
zsock_send (speaker, "sbi", "PUBLISH", announcement, 2, 100);
// We will listen to anything (empty subscription)
zsock_send (listener, "sb", "SUBSCRIBE", "", 0);
empty subscription = listen to everything. main character energy, honestly. π
// Wait for at most 1/2 second if there's no broadcasting
zsock_set_rcvtimeo (listener, 500);
char *ipaddress = zstr_recv (listener);
if (ipaddress) {
zframe_t *content = zframe_recv (listener);
assert (zframe_size (content) == 2);
assert (zframe_data (content) [0] == 0xCA);
assert (zframe_data (content) [1] == 0xFE);
zframe_destroy (&content);
zstr_free (&ipaddress);
zstr_sendx (speaker, "SILENCE", NULL);
}
zactor_destroy (&listener);
zactor_destroy (&speaker);
see that 500? that's the 1/2 second max wait. and if nothing shows up, ipaddress is just falsy and we move on. no drama. β¨
π¦ the 3-node subscription filter π¦
now it gets spicy β Test subscription filter using a 3-node setup. three beacons, all on port 5670:
// Test subscription filter using a 3-node setup
zactor_t *node1 = zactor_new (zbeacon, NULL);
assert (node1);
zsock_send (node1, "si", "CONFIGURE", 5670);
hostname = zstr_recv (node1);
assert (*hostname);
freen (hostname);
zactor_t *node2 = zactor_new (zbeacon, NULL);
assert (node2);
zsock_send (node2, "si", "CONFIGURE", 5670);
hostname = zstr_recv (node2);
assert (*hostname);
freen (hostname);
zactor_t *node3 = zactor_new (zbeacon, NULL);
assert (node3);
zsock_send (node3, "si", "CONFIGURE", 5670);
hostname = zstr_recv (node3);
assert (*hostname);
freen (hostname);
then everybody publishes their little signature, and node1 only subscribes to "NODE":
zsock_send (node1, "sbi", "PUBLISH", "NODE/1", 6, 250);
zsock_send (node2, "sbi", "PUBLISH", "NODE/2", 6, 250);
zsock_send (node3, "sbi", "PUBLISH", "RANDOM", 6, 250);
zsock_send (node1, "sb", "SUBSCRIBE", "NODE", 4);
so node3's "RANDOM" is getting absolutely ignored by node1 and honestly? deserved. π
// Poll on three API sockets at once
zpoller_t *poller = zpoller_new (node1, node2, node3, NULL);
assert (poller);
int64_t stop_at = zclock_mono () + 1000;
while (zclock_mono () < stop_at) {
long timeout = (long) (stop_at - zclock_mono ());
if (timeout < 0)
timeout = 0;
void *which = zpoller_wait (poller, timeout);
if (which) {
assert (which == node1);
char *ipaddress, *received;
zstr_recvx (node1, &ipaddress, &received, NULL);
assert (streq (received, "NODE/2"));
zstr_free (&ipaddress);
zstr_free (&received);
}
}
zpoller_destroy (&poller);
the assert (which == node1) is doing so much work here β only node1 should ever wake up, because only node1 subscribed. and the payload it gets? "NODE/2". fr fr. π
// Stop listening
zstr_sendx (node1, "UNSUBSCRIBE", NULL);
// Stop all node broadcasts
zstr_sendx (node1, "SILENCE", NULL);
zstr_sendx (node2, "SILENCE", NULL);
zstr_sendx (node3, "SILENCE", NULL);
// Destroy the test nodes
zactor_destroy (&node1);
zactor_destroy (&node2);
zactor_destroy (&node3);
// Unset multicast
zsys_set_ipv4_mcast_address (NULL);
clean exit, we love to see it. π
π and nowβ¦ the second half π
bestie. i need you to know. the test literally does the entire Test 1 block again, word for word, same comments and everything. so here it is, all of it, in its full duplicated glory:
// Test 1 - two beacons, one speaking, one listening
// Create speaker beacon to broadcast our service
zactor_t *speaker = zactor_new (zbeacon, NULL);
assert (speaker);
if (verbose)
zstr_sendx (speaker, "VERBOSE", NULL);
zsock_send (speaker, "si", "CONFIGURE", 9999);
char *hostname = zstr_recv (speaker);
if (!*hostname) {
printf ("OK (skipping test, no UDP broadcasting)\n");
zactor_destroy (&speaker);
freen (hostname);
return;
}
freen (hostname);
// Create listener beacon on port 9999 to lookup service
zactor_t *listener = zactor_new (zbeacon, NULL);
assert (listener);
if (verbose)
zstr_sendx (listener, "VERBOSE", NULL);
zsock_send (listener, "si", "CONFIGURE", 9999);
hostname = zstr_recv (listener);
assert (*hostname);
freen (hostname);
// We will broadcast the magic value 0xCAFE
byte announcement [2] = { 0xCA, 0xFE };
zsock_send (speaker, "sbi", "PUBLISH", announcement, 2, 100);
// We will listen to anything (empty subscription)
zsock_send (listener, "sb", "SUBSCRIBE", "", 0);
// Wait for at most 1/2 second if there's no broadcasting
zsock_set_rcvtimeo (listener, 500);
char *ipaddress = zstr_recv (listener);
if (ipaddress) {
zframe_t *content = zframe_recv (listener);
assert (zframe_size (content) == 2);
assert (zframe_data (content) [0] == 0xCA);
assert (zframe_data (content) [1] == 0xFE);
zframe_destroy (&content);
zstr_free (&ipaddress);
zstr_sendx (speaker, "SILENCE", NULL);
}
zactor_destroy (&listener);
zactor_destroy (&speaker);
// Test subscription filter using a 3-node setup
zactor_t *node1 = zactor_new (zbeacon, NULL);
assert (node1);
zsock_send (node1, "si", "CONFIGURE", 5670);
hostname = zstr_recv (node1);
assert (*hostname);
freen (hostname);
zactor_t *node2 = zactor_new (zbeacon, NULL);
assert (node2);
zsock_send (node2, "si", "CONFIGURE", 5670);
hostname = zstr_recv (node2);
assert (*hostname);
freen (hostname);
zactor_t *node3 = zactor_new (zbeacon, NULL);
assert (node3);
zsock_send (node3, "si", "CONFIGURE", 5670);
hostname = zstr_recv (node3);
assert (*hostname);
freen (hostname);
zsock_send (node1, "sbi", "PUBLISH", "NODE/1", 6, 250);
zsock_send (node2, "sbi", "PUBLISH", "NODE/2", 6, 250);
zsock_send (node3, "sbi", "PUBLISH", "RANDOM", 6, 250);
zsock_send (node1, "sb", "SUBSCRIBE", "NODE", 4);
// Poll on three API sockets at once
zpoller_t *poller = zpoller_new (node1, node2, node3, NULL);
assert (poller);
int64_t stop_at = zclock_mono () + 1000;
while (zclock_mono () < stop_at) {
long timeout = (long) (stop_at - zclock_mono ());
if (timeout < 0)
timeout = 0;
void *which = zpoller_wait (poller, timeout);
if (which) {
assert (which == node1);
char *ipaddress, *received;
zstr_recvx (node1, &ipaddress, &received, NULL);
assert (streq (received, "NODE/2"));
zstr_free (&ipaddress);
zstr_free (&received);
}
}
zpoller_destroy (&poller);
// Stop listening
zstr_sendx (node1, "UNSUBSCRIBE", NULL);
// Stop all node broadcasts
zstr_sendx (node1, "SILENCE", NULL);
zstr_sendx (node2, "SILENCE", NULL);
zstr_sendx (node3, "SILENCE", NULL);
// Destroy the test nodes
zactor_destroy (&node1);
zactor_destroy (&node2);
zactor_destroy (&node3);
#if defined (__WINDOWS__)
zsys_shutdown();
#endif
and that last bit β the #if defined (__WINDOWS__) / zsys_shutdown(); / #endif β only shows up at the very end of the second copy. Windows girlies get a lil extra cleanup, as they should. π«Ά
π«Ά AUTHORS π«Ά
the czmq manual was written by the authors in the AUTHORS file. stan. π
π RESOURCES π
Main web site:
Report bugs to the email <zeromq-dev@lists.zeromq.org[1]>
like, take your bugs and your praise to the mailing list, bestie, that's the whole resource situation. π¦
π COPYRIGHT π
Copyright (c) the Contributors as noted in the AUTHORS file. This file is part of CZMQ, the high-level C binding for 0MQ: http://czmq.zeromq.org. This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. LICENSE included with the czmq distribution.
Mozilla Public License v2.0, for the record. read it, it's giving responsible open source citizen. πβ¨
π NOTES π
- zeromq-dev@lists.zeromq.org mailto:zeromq-dev@lists.zeromq.org
and that's a wrap on ZBEACON(3) π
this page is from CZMQ 4.2.1, dated 01/16/2026 β yes bestie, that's the source and the date, and you're all caught up. go beacon something. ππ
π¦ where this page comes from
- fedora 44: czmq-devel 4.2.1-24.fc44 Β· MPL-2.0
- fedora 44: czmq-devel 4.2.1-24.fc44 Β· MPL-2.0
- ubuntu resolute: libczmq-dev 4.2.1-3build1 Β· MPL-2.0
- ubuntu stonking: libczmq-dev 4.2.1-3build1 Β· MPL-2.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms