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 a zmsg_t object, or NULL if the recv was interrupted. Heads up: this does a blocking recv. If you do not want to block, then use the zloop class, or zmsg_recv_nowait, or zmq_poll to 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. Returns NULL if the message could not be loaded. ๐Ÿ˜ญ
  • zmsg_decode() โ€” decodes a serialized message frame that was created by zmsg_encode (), and returns a new zmsg_t object. Returns NULL if 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, 0 means 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 a zmsg_sendm, you must call zmsg_send or 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 (0 or 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. Returns 0 on success, -1 on error. This deprecates zmsg_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. Returns 0 on success. This deprecates zmsg_add, which did not nullify the caller's frame reference.
  • zmsg_pop() โ€” removes the first frame from the message, if any. Returns the frame, or NULL. 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. Returns 0 on success, -1 on error.
  • zmsg_addmem() โ€” adds a block of memory to the end of the message, as a new frame. Returns 0 on success, -1 on error.
  • zmsg_pushstr() โ€” pushes a string as a new frame to the front of the message. Returns 0 on success, -1 on error.
  • zmsg_addstr() โ€” pushes a string as a new frame to the end of the message. Returns 0 on success, -1 on error.
  • zmsg_pushstrf() โ€” pushes a formatted string as a new frame to the front of the message. Returns 0 on success, -1 on error. Note the CHECK_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. Returns 0 on success, -1 on error. Also CHECK_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, returns NULL. 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. Returns 0 on success, -1 on error. It's giving โœจnestedโœจ.
  • zmsg_popmsg() โ€” removes the first submessage from the message, if any. Returns a zmsg_t, or NULL if 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, or NULL if 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, returns NULL. To move to the first frame, call zmsg_first(). This advances the cursor.
  • zmsg_last() โ€” returns the last frame. If there are no frames, returns NULL.

๐Ÿ’พ 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, returns 0 if 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, use zmsg_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 fresh zmsg_t object. 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 by zsys_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 with zframe_eq, returns false if either message is NULL.
  • zmsg_signal() โ€” returns the signal value, 0 or greater, if the message is a signal; -1 if it isn't.
  • zmsg_is() โ€” probes the supplied object and reports whether it looks like a zmsg_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 a ZMQ_SERVER socket. 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 a ZMQ_SERVER socket.
  • 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 by zsys_set_logstream). The message length is specified; no truncation unless length is zero. Backwards compatible with zframe_print when 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 (&copy, 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 (&copy);

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 (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 ๐Ÿ“

  1. 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