ZMSG(3)
๐ฆ NAME ๐ฆ
zmsg โ the class for working with multipart messages. It lives in the CZMQ Manual, section 3. C library energy, container vibes. ๐
โจ SYNOPSIS โจ
Okay bestie, first the important tea before we touch a single function:
This is a stable class, and it may not change except for emergencies. It's provided in stable builds. ๐ซถ
This class also has draft methods, which may change over time. They are not in stable releases, by default. Use --enable-drafts to enable them. (Yes, that's a configure flag, and yes, you have to actually opt in. ๐)
๐ฃ Making a message & grabbing one off a socket
CZMQ_EXPORT zmsg_t *
zmsg_new (void);
CZMQ_EXPORT zmsg_t *
zmsg_recv (void *source);
CZMQ_EXPORT zmsg_t *
zmsg_load (FILE *file);
CZMQ_EXPORT zmsg_t *
zmsg_decode (zframe_t *frame);
CZMQ_EXPORT zmsg_t *
zmsg_new_signal (byte status);
zmsg_new()โ creates a brand new empty message object. Blank slate, main character energy. โจzmsg_recv()โ receives a message from a socket and hands you back azmsg_tobject, orNULLif the recv was interrupted. Heads up: this does a blocking recv. If you do not want to block, then use thezloopclass, orzmsg_recv_nowait, orzmq_pollto check for socket input before receiving. Not me waiting forever on a socket ๐zmsg_load()โ loads/appends an open file into a new message, and returns the message. ReturnsNULLif the message could not be loaded. ๐ญzmsg_decode()โ decodes a serialized message frame that was created byzmsg_encode (), and returns a newzmsg_tobject. ReturnsNULLif the frame was badly formatted, or if there was insufficient memory to work. Lowkey a lot of ways to fail but okay.zmsg_new_signal()โ generates a signal message encoding the given status. A signal is a short message carrying a 1-byte success/failure code (by convention,0means OK). Signals are encoded to be distinguishable from "normal" messages. ๐ฆ
๐ค Sending & destroying
CZMQ_EXPORT void
zmsg_destroy (zmsg_t **self_p);
CZMQ_EXPORT int
zmsg_send (zmsg_t **self_p, void *dest);
CZMQ_EXPORT int
zmsg_sendm (zmsg_t **self_p, void *dest);
zmsg_destroy()โ destroys a message object and all the frames it contains. Everything goes, no survivors. ๐zmsg_send()โ sends the message to the destination socket, and destroys the message after sending it successfully. If the message has no frames, it sends nothing but destroys the message anyhow. It also nullifies the caller's reference to the message, because it's a destructor. Read that again, bestie: your pointer is toast after this call.zmsg_sendm()โ sends the message to the destination socket as part of a multipart sequence, and destroys the message after sending it successfully. Note that after azmsg_sendm, you must callzmsg_sendor another method that sends a final message part. If the message has no frames, it sends nothing but destroys the message anyhow. Nullifies the caller's reference to the message (as it is a destructor). Don't leave a multipart hanging, fr fr. ๐ซ
๐งฎ Sizing it up
CZMQ_EXPORT size_t
zmsg_size (zmsg_t *self);
CZMQ_EXPORT size_t
zmsg_content_size (zmsg_t *self);
zmsg_size()โ returns the size of the message, i.e. the number of frames (0or more).zmsg_content_size()โ returns the total size of all frames in the message. The whole vibe check, measured. ๐
๐งฉ Frames: in the front, out the front
CZMQ_EXPORT int
zmsg_prepend (zmsg_t *self, zframe_t **frame_p);
CZMQ_EXPORT int
zmsg_append (zmsg_t *self, zframe_t **frame_p);
CZMQ_EXPORT zframe_t *
zmsg_pop (zmsg_t *self);
zmsg_prepend()โ pushes a frame to the front of the message, i.e. before all other frames. The message takes ownership of the frame and will destroy it when the message is sent. Returns0on success,-1on error. This deprecateszmsg_push, which did not nullify the caller's frame reference. ๐zmsg_append()โ adds a frame to the end of the message, i.e. after all other frames. The message takes ownership of the frame and will destroy it when the message is sent. Returns0on success. This deprecateszmsg_add, which did not nullify the caller's frame reference.zmsg_pop()โ removes the first frame from the message, if any. Returns the frame, orNULL. The caller owns the return value and must destroy it when done. Your frame now, your responsibility. ๐ซก
๐ชข Memory blocks & strings
CZMQ_EXPORT int
zmsg_pushmem (zmsg_t *self, const void *data, size_t size);
CZMQ_EXPORT int
zmsg_addmem (zmsg_t *self, const void *data, size_t size);
CZMQ_EXPORT int
zmsg_pushstr (zmsg_t *self, const char *string);
CZMQ_EXPORT int
zmsg_addstr (zmsg_t *self, const char *string);
CZMQ_EXPORT int
zmsg_pushstrf (zmsg_t *self, const char *format, ...) CHECK_PRINTF (2);
CZMQ_EXPORT int
zmsg_addstrf (zmsg_t *self, const char *format, ...) CHECK_PRINTF (2);
CZMQ_EXPORT char *
zmsg_popstr (zmsg_t *self);
zmsg_pushmem()โ pushes a block of memory to the front of the message, as a new frame. Returns0on success,-1on error.zmsg_addmem()โ adds a block of memory to the end of the message, as a new frame. Returns0on success,-1on error.zmsg_pushstr()โ pushes a string as a new frame to the front of the message. Returns0on success,-1on error.zmsg_addstr()โ pushes a string as a new frame to the end of the message. Returns0on success,-1on error.zmsg_pushstrf()โ pushes a formatted string as a new frame to the front of the message. Returns0on success,-1on error. Note theCHECK_PRINTF (2)โ the compiler is watching your format string, so no funny business. ๐zmsg_addstrf()โ pushes a formatted string as a new frame to the end of the message. Returns0on success,-1on error. AlsoCHECK_PRINTF (2).zmsg_popstr()โ pops a frame off the front of the message and returns it as a fresh string. If there were no more frames in the message, returnsNULL. The caller owns the return value and must destroy it when done. ๐
๐ Submessages & removals
CZMQ_EXPORT int
zmsg_addmsg (zmsg_t *self, zmsg_t **msg_p);
CZMQ_EXPORT zmsg_t *
zmsg_popmsg (zmsg_t *self);
CZMQ_EXPORT void
zmsg_remove (zmsg_t *self, zframe_t *frame);
zmsg_addmsg()โ pushes an encoded message as a new frame. The message takes ownership of the submessage, so the original is destroyed in this call. Returns0on success,-1on error. It's giving โจnestedโจ.zmsg_popmsg()โ removes the first submessage from the message, if any. Returns azmsg_t, orNULLif decoding was not successful. The caller owns the return value and must destroy it when done.zmsg_remove()โ removes the specified frame from the list, if present. Does not destroy the frame. Rude to remove it, ruder to destroy it. ๐
๐งญ Cursor navigation
CZMQ_EXPORT zframe_t *
zmsg_first (zmsg_t *self);
CZMQ_EXPORT zframe_t *
zmsg_next (zmsg_t *self);
CZMQ_EXPORT zframe_t *
zmsg_last (zmsg_t *self);
zmsg_first()โ sets the cursor to the first frame in the message. Returns the frame, orNULLif the message is empty. Use this to navigate the frames as a list. ๐งzmsg_next()โ returns the next frame. If there are no more frames, returnsNULL. To move to the first frame, callzmsg_first(). This advances the cursor.zmsg_last()โ returns the last frame. If there are no frames, returnsNULL.
๐พ Saving, encoding & duplicating
CZMQ_EXPORT int
zmsg_save (zmsg_t *self, FILE *file);
CZMQ_EXPORT zframe_t *
zmsg_encode (zmsg_t *self);
CZMQ_EXPORT zmsg_t *
zmsg_dup (zmsg_t *self);
zmsg_save()โ saves the message to an open file, returns0if OK, else-1. The message is saved as a series of frames, each with length and data. > โ ๏ธ Note that the file is NOT guaranteed to be portable between operating systems, not versions of CZMQ. The file format is at present undocumented and liable to arbitrary change. So like, don't build a whole archive format on this, bestie. ๐zmsg_encode()โ serializes a multipart message to a single message frame. Use this method to send structured messages across transports that do not support multipart data. It allocates and returns a new frame containing the serialized message. To decode a serialized message frame, usezmsg_decode (). The caller owns the return value and must destroy it when done. โจzmsg_dup()โ creates a copy of the message, as a new message object. Returns a freshzmsg_tobject. If the message is null, or memory was exhausted, returns null. The caller owns the return value and must destroy it when done. Twin energy. ๐ฏ
๐จ๏ธ Printing, comparing, signals, is & test
CZMQ_EXPORT void
zmsg_print (zmsg_t *self);
CZMQ_EXPORT bool
zmsg_eq (zmsg_t *self, zmsg_t *other);
CZMQ_EXPORT int
zmsg_signal (zmsg_t *self);
CZMQ_EXPORT bool
zmsg_is (void *self);
CZMQ_EXPORT void
zmsg_test (bool verbose);
zmsg_print()โ sends the message to the zsys log sink (may be stdout, or a system facility as configured byzsys_set_logstream). Long messages are truncated. ๐ญzmsg_eq()โ returns true if the two messages have the same number of frames and each frame in the first message is identical to the corresponding frame in the other message. As withzframe_eq, returns false if either message isNULL.zmsg_signal()โ returns the signal value,0or greater, if the message is a signal;-1if it isn't.zmsg_is()โ probes the supplied object and reports whether it looks like azmsg_t. Detective work. ๐zmsg_test()โ self test of this class. Go off, little test suite. ๐งช
๐ฑ Draft methods (may change without warning!)
These lil guys are only there if you built with drafts enabled. Wrapped up like this:
#ifdef CZMQ_BUILD_DRAFT_API
CZMQ_EXPORT uint32_t
zmsg_routing_id (zmsg_t *self);
CZMQ_EXPORT void
zmsg_set_routing_id (zmsg_t *self, uint32_t routing_id);
CZMQ_EXPORT void
zmsg_print_n (zmsg_t *self, size_t size);
#endif // CZMQ_BUILD_DRAFT_API
zmsg_routing_id()โ * Draft method, for development use, may change without warning * Returns the message routing ID, if the message came from aZMQ_SERVERsocket. Otherwise returns zero.zmsg_set_routing_id()โ * Draft method, for development use, may change without warning * Sets the routing ID on the message. This is used if/when the message is sent to aZMQ_SERVERsocket.zmsg_print_n()โ * Draft method, for development use, may change without warning * Sends the message to the zsys log sink (may be stdout, or a system facility as configured byzsys_set_logstream). The message length is specified; no truncation unless length is zero. Backwards compatible withzframe_printwhen length is zero.
Please add '@interface' section in './../src/zmsg.c'.
๐ธ DESCRIPTION ๐ธ
The zmsg class provides methods to send and receive multipart messages across 0MQ sockets. This class provides a list-like container interface, with methods to work with the overall container. zmsg_t messages are composed of zero or more zframe_t frames. Zero frames? Valid. Twelve frames? Also valid. It's a container, she contains. ๐
Please add @discuss section in ./../src/zmsg.c.
๐ EXAMPLE ๐
okay bestie, this is the whole vibe, straight from the zmsg_test method. it's giving "show don't tell" and honestly? iconic. read it top to bottom, it's basically a whole tour of the API in one go ๐ซถ
// Create two PAIR sockets and connect over inproc
zsock_t *output = zsock_new_pair ("@inproc://zmsg.test");
assert (output);
zsock_t *input = zsock_new_pair (">inproc://zmsg.test");
assert (input);
// Test send and receive of single-frame message
zmsg_t *msg = zmsg_new ();
assert (msg);
zframe_t *frame = zframe_new ("Hello", 5);
assert (frame);
zmsg_prepend (msg, &frame);
assert (zmsg_size (msg) == 1);
assert (zmsg_content_size (msg) == 5);
rc = zmsg_send (&msg, output);
assert (msg == NULL);
assert (rc == 0);
msg = zmsg_recv (input);
assert (msg);
assert (zmsg_size (msg) == 1);
assert (zmsg_content_size (msg) == 5);
zmsg_destroy (&msg);
// Test send and receive of multi-frame message
msg = zmsg_new ();
assert (msg);
rc = zmsg_addmem (msg, "Frame0", 6);
assert (rc == 0);
rc = zmsg_addmem (msg, "Frame1", 6);
assert (rc == 0);
rc = zmsg_addmem (msg, "Frame2", 6);
assert (rc == 0);
rc = zmsg_addmem (msg, "Frame3", 6);
assert (rc == 0);
rc = zmsg_addmem (msg, "Frame4", 6);
assert (rc == 0);
rc = zmsg_addmem (msg, "Frame5", 6);
assert (rc == 0);
rc = zmsg_addmem (msg, "Frame6", 6);
assert (rc == 0);
rc = zmsg_addmem (msg, "Frame7", 6);
assert (rc == 0);
rc = zmsg_addmem (msg, "Frame8", 6);
assert (rc == 0);
rc = zmsg_addmem (msg, "Frame9", 6);
assert (rc == 0);
zmsg_t *copy = zmsg_dup (msg);
assert (copy);
rc = zmsg_send (©, output);
assert (rc == 0);
rc = zmsg_send (&msg, output);
assert (rc == 0);
copy = zmsg_recv (input);
assert (copy);
assert (zmsg_size (copy) == 10);
assert (zmsg_content_size (copy) == 60);
zmsg_destroy (©);
msg = zmsg_recv (input);
assert (msg);
assert (zmsg_size (msg) == 10);
assert (zmsg_content_size (msg) == 60);
// Save to a file, read back
FILE *file = fopen ("zmsg.test", "w");
assert (file);
rc = zmsg_save (msg, file);
assert (rc == 0);
fclose (file);
file = fopen ("zmsg.test", "r");
rc = zmsg_save (msg, file);
assert (rc == -1);
fclose (file);
zmsg_destroy (&msg);
file = fopen ("zmsg.test", "r");
msg = zmsg_load (file);
assert (msg);
fclose (file);
remove ("zmsg.test");
assert (zmsg_size (msg) == 10);
assert (zmsg_content_size (msg) == 60);
// Remove all frames except first and last
int frame_nbr;
for (frame_nbr = 0; frame_nbr < 8; frame_nbr++) {
zmsg_first (msg);
frame = zmsg_next (msg);
zmsg_remove (msg, frame);
zframe_destroy (&frame);
}
// Test message frame manipulation
assert (zmsg_size (msg) == 2);
frame = zmsg_last (msg);
assert (zframe_streq (frame, "Frame9"));
assert (zmsg_content_size (msg) == 12);
frame = zframe_new ("Address", 7);
assert (frame);
zmsg_prepend (msg, &frame);
assert (zmsg_size (msg) == 3);
rc = zmsg_addstr (msg, "Body");
assert (rc == 0);
assert (zmsg_size (msg) == 4);
frame = zmsg_pop (msg);
zframe_destroy (&frame);
assert (zmsg_size (msg) == 3);
char *body = zmsg_popstr (msg);
assert (streq (body, "Frame0"));
freen (body);
zmsg_destroy (&msg);
// Test encoding/decoding
msg = zmsg_new ();
assert (msg);
byte *blank = (byte *) zmalloc (100000);
assert (blank);
rc = zmsg_addmem (msg, blank, 0);
assert (rc == 0);
rc = zmsg_addmem (msg, blank, 1);
assert (rc == 0);
rc = zmsg_addmem (msg, blank, 253);
assert (rc == 0);
rc = zmsg_addmem (msg, blank, 254);
assert (rc == 0);
rc = zmsg_addmem (msg, blank, 255);
assert (rc == 0);
rc = zmsg_addmem (msg, blank, 256);
assert (rc == 0);
rc = zmsg_addmem (msg, blank, 65535);
assert (rc == 0);
rc = zmsg_addmem (msg, blank, 65536);
assert (rc == 0);
rc = zmsg_addmem (msg, blank, 65537);
assert (rc == 0);
freen (blank);
assert (zmsg_size (msg) == 9);
frame = zmsg_encode (msg);
zmsg_destroy (&msg);
msg = zmsg_decode (frame);
assert (msg);
zmsg_destroy (&msg);
zframe_destroy (&frame);
// Test submessages
msg = zmsg_new ();
assert (msg);
zmsg_t *submsg = zmsg_new ();
zmsg_pushstr (msg, "matr");
zmsg_pushstr (submsg, "joska");
rc = zmsg_addmsg (msg, &submsg);
assert (rc == 0);
assert (submsg == NULL);
submsg = zmsg_popmsg (msg);
assert (submsg == NULL); // string "matr" is not encoded zmsg_t, so was discarded
submsg = zmsg_popmsg (msg);
assert (submsg);
body = zmsg_popstr (submsg);
assert (streq (body, "joska"));
freen (body);
zmsg_destroy (&submsg);
frame = zmsg_pop (msg);
assert (frame == NULL);
zmsg_destroy (&msg);
// Test comparison of two messages
msg = zmsg_new ();
zmsg_addstr (msg, "One");
zmsg_addstr (msg, "Two");
zmsg_addstr (msg, "Three");
zmsg_t *msg_other = zmsg_new ();
zmsg_addstr (msg_other, "One");
zmsg_addstr (msg_other, "Two");
zmsg_addstr (msg_other, "One-Hundred");
zmsg_t *msg_dup = zmsg_dup (msg);
zmsg_t *empty_msg = zmsg_new ();
zmsg_t *empty_msg_2 = zmsg_new ();
assert (zmsg_eq (msg, msg_dup));
assert (!zmsg_eq (msg, msg_other));
assert (zmsg_eq (empty_msg, empty_msg_2));
assert (!zmsg_eq (msg, NULL));
assert (!zmsg_eq (NULL, empty_msg));
assert (!zmsg_eq (NULL, NULL));
zmsg_destroy (&msg);
zmsg_destroy (&msg_other);
zmsg_destroy (&msg_dup);
zmsg_destroy (&empty_msg);
zmsg_destroy (&empty_msg_2);
// Test signal messages
msg = zmsg_new_signal (0);
assert (zmsg_signal (msg) == 0);
zmsg_destroy (&msg);
msg = zmsg_new_signal (-1);
assert (zmsg_signal (msg) == 255);
zmsg_destroy (&msg);
// Now try methods on an empty message
msg = zmsg_new ();
assert (msg);
assert (zmsg_size (msg) == 0);
assert (zmsg_unwrap (msg) == NULL);
assert (zmsg_first (msg) == NULL);
assert (zmsg_last (msg) == NULL);
assert (zmsg_next (msg) == NULL);
assert (zmsg_pop (msg) == NULL);
// Sending an empty message is valid and destroys the message
assert (zmsg_send (&msg, output) == 0);
assert (!msg);
zsock_destroy (&input);
zsock_destroy (&output);
#if defined (ZMQ_SERVER)
// Create server and client sockets and connect over inproc
zsock_t *server = zsock_new_server ("inproc://zmsg-test-routing");
assert (server);
zsock_t *client = zsock_new_client ("inproc://zmsg-test-routing");
assert (client);
// Send request from client to server
zmsg_t *request = zmsg_new ();
assert (request);
zmsg_addstr (request, "Hello");
rc = zmsg_send (&request, client);
assert (rc == 0);
assert (!request);
// Read request and send reply
request = zmsg_recv (server);
assert (request);
char *string = zmsg_popstr (request);
assert (streq (string, "Hello"));
assert (zmsg_routing_id (request));
zstr_free (&string);
zmsg_t *reply = zmsg_new ();
assert (reply);
zmsg_addstr (reply, "World");
zmsg_set_routing_id (reply, zmsg_routing_id (request));
rc = zmsg_send (&reply, server);
assert (rc == 0);
zmsg_destroy (&request);
// Read reply
reply = zmsg_recv (client);
string = zmsg_popstr (reply);
assert (streq (string, "World"));
assert (zmsg_routing_id (reply) == 0);
zmsg_destroy (&reply);
zstr_free (&string);
// Client and server disallow multipart
msg = zmsg_new ();
zmsg_addstr (msg, "One");
zmsg_addstr (msg, "Two");
rc = zmsg_send (&msg, client);
assert (rc == -1);
assert (zmsg_size (msg) == 2);
rc = zmsg_send (&msg, server);
assert (rc == -1);
assert (zmsg_size (msg) == 2);
zmsg_destroy (&msg);
zsock_destroy (&client);
zsock_destroy (&server);
#endif
// Test message length calculation after removal
msg = zmsg_new ();
zmsg_addstr (msg, "One");
zmsg_addstr (msg, "Two");
size_t size_before = zmsg_content_size (msg);
frame = zframe_new ("Three", strlen ("Three"));
assert (frame);
zmsg_remove (msg, frame);
size_t size_after = zmsg_content_size (msg);
assert (size_before == size_after);
zframe_destroy (&frame);
zmsg_destroy (&msg);
#if defined (__WINDOWS__)
zsys_shutdown();
#endif
not me reading a test method like it's a novel ๐ญ but seriously, that's the whole tour: pairs, sends, recvs, saves, loads, pops, encodes, submsgs, comparisons, signals, the empty-message edge cases, the ZMQ_SERVER client/server bit behind its #if defined (ZMQ_SERVER), and the sneaky little length-calculation check at the end. and yes, that #if defined (__WINDOWS__) / zsys_shutdown(); / #endif is exactly how it ends. periodt. ๐
๐ AUTHORS ๐
the czmq manual was written by the authors in the AUTHORS file. it takes a village bestie, and that village is listed in there ๐ซถ
๐ RESOURCES ๐
Main web site:
report bugs to the email zeromq-dev@lists.zeromq.org[1] โ found a bug? tell the nice people. they're listening, and honestly they want to know ๐ฆ
๐ 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.
ngl bestie, the license text is the one part you actually don't vibe-rewrite. it's legal, it's verbatim, and it ate. ๐
๐ NOTES ๐
zeromq-dev@lists.zeromq.orgโmailto:zeromq-dev@lists.zeromq.org
and that's the page!! ๐ this was CZMQ 4.2.1, dated 01/16/2026 โ so you know it's fresh. go forth and sling some messages, bestie, you've got this ๐โจ
๐ฆ 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