ZFRAME(3)

πŸ’– NAME πŸ’–

okay so hi bestie 🫢 welcome to ZFRAME(3), and yeah, this whole thing lives in the CZMQ Manual. Here's the ✨official✨ one-liner, no edits:

zframe β€” Class for working with single message frames πŸ¦‹


✨ SYNOPSIS ✨

vibe check first, because it matters πŸ‘‘ β€” this is a stable class, and it may not change except for emergencies. It's provided in stable builds. iconic. we love a stable queen.

BUT. Plot twist 😭 β€” this class also has draft methods, which may change over time. They are not in stable releases, by default. Wanna unlock them? Use --enable-drafts to enable. That's the whole key πŸ”‘

πŸŽ€ the macros πŸŽ€

three lil constants, and ngl the source ships them with empty comments, so I'm not about to make stuff up about them. Here they are, untouched, in all their glory πŸ’…

  • #define ZFRAME_MORE 1
  • #define ZFRAME_REUSE 2
  • #define ZFRAME_DONTWAIT 4

🧚 the functions 🧚

okay here's the whole roster, one by one, signature and all. no skips, no cuts, periodt.

  • CZMQ_EXPORT zframe_t * zframe_new (const void *data, size_t size); β€” new frame, who dis πŸ’–. If size is not null, it allocates the frame data to the size you asked for. And if data is also not null, it copies size octets from that data straight into the frame body. boom, frame.
  • CZMQ_EXPORT zframe_t * zframe_new_empty (void); β€” makes you an empty frame. Zero-sized. Absolutely nothing in it. It's giving minimalist πŸŽ€
  • CZMQ_EXPORT zframe_t * zframe_from (const char *string); β€” same energy but with a string in it, so your frame has content.
  • CZMQ_EXPORT zframe_t * zframe_recv (void *source); β€” pulls a frame off a socket, returns a zframe_t object, or NULL if the recv got interrupted. Heads up bestie: this does a blocking recv. If you do NOT want to block, use zpoller or zloop instead πŸ™ˆ
  • CZMQ_EXPORT void zframe_destroy (zframe_t **self_p); β€” destroy a frame. bye girl πŸ’€
  • CZMQ_EXPORT int zframe_send (zframe_t **self_p, void *dest, int flags); β€” sends a frame to a socket, and then destroys the frame after sending. Returns -1 on error, 0 on success. no cap.
  • CZMQ_EXPORT size_t zframe_size (zframe_t *self); β€” how many bytes are in the frame data? that's the number πŸ“
  • CZMQ_EXPORT byte * zframe_data (zframe_t *self); β€” hands you the address of the frame data.
  • CZMQ_EXPORT const char * zframe_meta (zframe_t *self, const char *property); β€” returns the meta data property for the frame. ⚠️ and listen: the caller shall not modify or free the returned value, because it shall be owned by the message. hands off, it's not yours 🫢
  • CZMQ_EXPORT zframe_t * zframe_dup (zframe_t *self); β€” makes a brand new frame that duplicates an existing frame. If frame is null, or memory was exhausted, it returns null. Also β€” caller owns return value and must destroy it when done πŸ‘‘
  • CZMQ_EXPORT char * zframe_strhex (zframe_t *self); β€” returns the frame data encoded as a printable hex string. Super useful for 0MQ UUIDs, fr fr. Caller must free string when finished with it. And yeah β€” caller owns return value and must destroy it when done.
  • CZMQ_EXPORT char * zframe_strdup (zframe_t *self); β€” returns the frame data copied into a freshly allocated string. Caller must free string when finished with it. And also β€” caller owns return value and must destroy it when done.
  • CZMQ_EXPORT bool zframe_streq (zframe_t *self, const char *string); β€” returns TRUE if the frame body equals string, excluding the terminator. πŸ’…
  • CZMQ_EXPORT int zframe_more (zframe_t *self); β€” returns the frame's MORE indicator (that's 1 or 0). It gets set when you read a frame from a socket, or by the zframe_set_more() method.
  • CZMQ_EXPORT void zframe_set_more (zframe_t *self, int more); β€” sets the frame MORE indicator (1 or 0). 🚨 important: this is NOT used when sending a frame to a socket β€” you have to specify the flag explicitly. do not forget. I'm serious 😭
  • CZMQ_EXPORT bool zframe_eq (zframe_t *self, zframe_t *other); β€” TRUE if two frames have identical size and data. And if either frame is NULL, equality is always false. no exceptions bestie.
  • CZMQ_EXPORT void zframe_reset (zframe_t *self, const void *data, size_t size); β€” slaps brand new contents onto the frame. glow-up ✨
  • CZMQ_EXPORT void zframe_print (zframe_t *self, const char *prefix); β€” sends the message to the zsys log sink (which may be stdout, or a system facility, as configured by zsys_set_logstream). prefix shows before the frame, if it's not null. And heads up: long messages are truncated βœ‚οΈ
  • CZMQ_EXPORT bool zframe_is (void *self); β€” probes the object you hand it and reports whether it looks like a zframe_t. detective energy πŸ”
  • CZMQ_EXPORT void zframe_test (bool verbose); β€” self test of this class. run it, love it πŸ’–

πŸŒ€ the draft era πŸŒ€

everything in here is gated behind #ifdef CZMQ_BUILD_DRAFT_API, and every single one of them is a draft method, for development use, may change without warning. so like, enjoy but don't get attached 😭

  • typedef void (zframe_destructor_fn) (void **hint); β€” Destroy an item.
  • CZMQ_EXPORT zframe_t * zframe_frommem (void *data, size_t size, zframe_destructor_fn destructor, void *hint); β€” creates a new frame from memory. It takes ownership of the memory and calls the destructor on destroy. 🫢
  • CZMQ_EXPORT uint32_t zframe_routing_id (zframe_t *self); β€” returns the frame's routing ID, if the frame came from a ZMQ_SERVER socket. Otherwise it returns zero.
  • CZMQ_EXPORT void zframe_set_routing_id (zframe_t *self, uint32_t routing_id); β€” sets a routing ID on the frame. This is used if/when the frame gets sent to a ZMQ_SERVER socket.
  • CZMQ_EXPORT const char * zframe_group (zframe_t *self); β€” returns the frame group of the radio-dish pattern πŸ“»
  • CZMQ_EXPORT int zframe_set_group (zframe_t *self, const char *group); β€” sets the group on the frame. This is used if/when the frame is sent to a ZMQ_RADIO socket. Returns -1 on error, 0 on success.
  • CZMQ_EXPORT void zframe_print_n (zframe_t *self, const char *prefix, size_t length); β€” sends the message to the zsys log sink (may be stdout, or a system facility as configured by zsys_set_logstream). prefix shows before the frame, if it's not null. Here the message length is specified, and there's no truncation unless length is zero. And it's backwards compatible with zframe_print when length is zero πŸ‘‘
one more thing before we move on, and I'm quoting this exactly: Please add '@interface' section in './../src/zframe.c'.

🌸 DESCRIPTION 🌸

so here's the tea β˜• the zframe class gives you methods to send and receive single message frames across 0MQ sockets. One frame corresponds to one zmq_msg_t. that's the whole relationship πŸ’–

When you read a frame from a socket, the zframe_more() method tells you if that frame is part of an unfinished multipart message. So if you're sitting there like "wait, is there more coming??" β€” that's the girl you ask πŸ™ˆ

The zframe_send method normally destroys the frame. BUT β€” and this is the plot twist β€” with the ZFRAME_REUSE flag, you can send the same frame many times. reuse queen πŸ‘‘

And a real-talk heads up: frames are binary, and this class has no special support for text data. so don't come here expecting cute little strings, bestie, it's bytes all the way down πŸ’€

also, verbatim from the source: Please add @discuss section in ./../src/zframe.c.

🌈 EXAMPLE 🌈

okay bestie, this is the whole zframe_test method, the actual receipts πŸ’… it's long but it's iconic, just read it top to bottom and you'll get the vibe.

//  Create two PAIR sockets and connect over TCP
zsock_t *output = zsock_new (ZMQ_PAIR);
assert (output);
int port = zsock_bind (output, "tcp://127.0.0.1:*");
assert (port != -1);
zsock_t *input = zsock_new (ZMQ_PAIR);
assert (input);
rc = zsock_connect (input, "tcp://127.0.0.1:%d", port);
assert (rc != -1);

//  Send five different frames, test ZFRAME_MORE
int frame_nbr;
for (frame_nbr = 0; frame_nbr < 5; frame_nbr++) {
    frame = zframe_new ("Hello", 5);
    assert (frame);
    rc = zframe_send (&frame, output, ZFRAME_MORE);
    assert (rc == 0);
}
//  Send same frame five times, test ZFRAME_REUSE
frame = zframe_new ("Hello", 5);
assert (frame);
for (frame_nbr = 0; frame_nbr < 5; frame_nbr++) {
    rc = zframe_send (&frame, output, ZFRAME_MORE + ZFRAME_REUSE);
    assert (rc == 0);
}
assert (frame);
zframe_t *copy = zframe_dup (frame);
assert (zframe_eq (frame, copy));
zframe_destroy (&frame);
assert (!zframe_eq (frame, copy));
assert (zframe_size (copy) == 5);
zframe_destroy (&copy);
assert (!zframe_eq (frame, copy));

//  Test zframe_new_empty
frame = zframe_new_empty ();
assert (frame);
assert (zframe_size (frame) == 0);
zframe_destroy (&frame);

//  Send END frame
frame = zframe_new ("NOT", 3);
assert (frame);
zframe_reset (frame, "END", 3);
char *string = zframe_strhex (frame);
assert (streq (string, "454E44"));
freen (string);
string = zframe_strdup (frame);
assert (streq (string, "END"));
freen (string);
rc = zframe_send (&frame, output, 0);
assert (rc == 0);

//  Read and count until we receive END
frame_nbr = 0;
for (frame_nbr = 0;; frame_nbr++) {
    zframe_t *frame = zframe_recv (input);
    if (zframe_streq (frame, "END")) {
        zframe_destroy (&frame);
        break;
    }
    assert (zframe_more (frame));
    zframe_set_more (frame, 0);
    assert (zframe_more (frame) == 0);
    zframe_destroy (&frame);
}
assert (frame_nbr == 10);

#if (ZMQ_VERSION >= ZMQ_MAKE_VERSION (4, 1, 0))
// Test zframe_meta
frame = zframe_new ("Hello", 5);
assert (frame);
rc = zframe_send (&frame, output, 0);
assert (rc == 0);
frame = zframe_recv (input);
const char *meta = zframe_meta (frame, "Socket-Type");
assert (meta != NULL);
assert (streq (meta, "PAIR"));
assert (zframe_meta (frame, "nonexistent") == NULL);
zframe_destroy (&frame);
#endif

zsock_destroy (&input);
zsock_destroy (&output);

okay and then, if you've got ZMQ_SERVER defined, we do the whole server/client thing over inproc 🫢

#if defined (ZMQ_SERVER)
//  Create server and client sockets and connect over inproc
zsock_t *server = zsock_new_server ("inproc://zframe-test-routing");
assert (server);
zsock_t *client = zsock_new_client ("inproc://zframe-test-routing");
assert (client);

//  Send request from client to server
zframe_t *request = zframe_new ("Hello", 5);
assert (request);
rc = zframe_send (&request, client, 0);
assert (rc == 0);
assert (!request);

//  Read request and send reply
request = zframe_recv (server);
assert (request);
assert (zframe_streq (request, "Hello"));
assert (zframe_routing_id (request));

zframe_t *reply = zframe_new ("World", 5);
assert (reply);
zframe_set_routing_id (reply, zframe_routing_id (request));
rc = zframe_send (&reply, server, 0);
assert (rc == 0);
zframe_destroy (&request);

//  Read reply
reply = zframe_recv (client);
assert (zframe_streq (reply, "World"));
assert (zframe_routing_id (reply) == 0);
zframe_destroy (&reply);

//  Client and server disallow multipart
frame = zframe_new ("Hello", 5);
rc = zframe_send (&frame, client, ZFRAME_MORE);
assert (rc == -1);
rc = zframe_send (&frame, server, ZFRAME_MORE);
assert (rc == -1);
zframe_destroy (&frame);

zsock_destroy (&client);
zsock_destroy (&server);
#endif

now the radio/dish bit 🌸 and note that if you don't have ZMQ_RADIO, you get an ENOTSUP instead, no cap:

#ifdef ZMQ_RADIO
//  Create radio and dish sockets and connect over inproc
zsock_t *radio = zsock_new_radio ("inproc://zframe-test-radio");
assert (radio);
zsock_t *dish = zsock_new_dish ("inproc://zframe-test-radio");
assert (dish);

//  Join the group
rc = zsock_join (dish, "World");
assert (rc == 0);

//  Publish message from radio
zframe_t *message = zframe_new ("Hello", 5);
assert (message);
rc = zframe_set_group (message, "World");
assert (rc == 0);
rc = zframe_send (&message, radio, 0);
assert (rc == 0);
assert (!message);

//  Receive the message from dish
message = zframe_recv (dish);
assert (message);
assert (zframe_streq (message, "Hello"));
assert (strcmp("World", zframe_group (message)) == 0);
zframe_destroy (&message);

zsock_destroy (&dish);
zsock_destroy (&radio);
#else
frame = zframe_new ("Hello", 5);
rc = zframe_set_group (frame, "World");
assert(rc == -1);
assert(errno == ENOTSUP);
zframe_destroy (&frame);
#endif

char str[] = "hello";
frame = zframe_frommem (str, 5, mem_destructor, str);
assert (frame);
zframe_destroy (&frame);

//  The destructor doesn't free the memory, only changing the strid,
//  so we can check if the destructor was invoked
assert (streq (str, "world"));

okay the print tests section, this one is so satisfying to read because every case has the expected output right there in the comments πŸ’–

// zframe_print tests

zsys_set_logstream(verbose ? stdout : NULL);

// == No data ==
frame = zframe_new ("", 0);

// no prefix, backwards compatible
//  - emits nothing but the timestamp
zframe_print (frame, "");
zframe_print_n (frame, "", 0);

// prefix, backwards compatible
//  - emits nothing but the timestamp
zframe_print (frame, "Prefix");
zframe_print_n (frame, "Prefix", 0);

// len > data
//  - emits nothing but the timestamp
zframe_print_n (frame, "", 15);
zframe_print_n (frame, "Prefix", 15);

// max len
//  - emits nothing but the timestamp
zframe_print_n (frame, "", -1);
zframe_print_n (frame, "Prefix", -1);

zframe_destroy (&frame);

// == Short data ==
frame = zframe_new ("Hello there!", 12);

// no prefix, backwards compatible: ellipsis
//  - "[012] Hello there!"
zframe_print (frame, "");
zframe_print_n (frame, "", 0);

// prefix, backwards compatible: ellipsis
//  - "Prefix[012] Hello there!"
zframe_print (frame, "Prefix");
zframe_print_n (frame, "Prefix", 0);

// len < data
//  - "[012] Hello"
//  - "Prefix[012] Hello"
zframe_print_n (frame, "", 5);
zframe_print_n (frame, "Prefix", 5);

// len > data
//  - "[012] Hello there!"
//  - "Prefix[012] Hello there!"
zframe_print_n (frame, "", 15);
zframe_print_n (frame, "Prefix", 15);

// max len
//  - "[012] Hello there!"
//  - "Prefix[012] Hello there!"
zframe_print_n (frame, "", -1);
zframe_print_n (frame, "Prefix", -1);

zframe_destroy (&frame);

// == Long data ==
frame = zframe_new ("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin finibus ligula et aliquam tristique. Phasellus consequat, enim et blandit varius, sapien diam faucibus lorem, non ultricies lacus turpis sed lectus. Vivamus id elit urna. In sit amet lacinia mauris. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Integer ut cursus diam. Vestibulum semper vel leo eu finibus. Ut urna magna, commodo vel auctor sed, eleifend quis lacus. Aenean quis ipsum et velit auctor ultrices.", 519);

// no prefix, backwards compatible: ellipsis
//  - "[070] Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin finibus..."
zframe_print (frame, "");
zframe_print_n (frame, "", 0);

// prefix, backwards compatible: ellipsis
//  - "Prefix[070] Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin finibus..."
zframe_print (frame, "Prefix");
zframe_print_n (frame, "Prefix", 0);

// len < data
//  - "[519] Lorem"
//  - "Prefix[519] Lorem"
zframe_print_n (frame, "", 5);
zframe_print_n (frame, "Prefix", 5);

// small len
//  - "[519] Lorem ipsum dolor sit amet"
//  - "Prefix[519] Lorem ipsum dolor sit amet"
zframe_print_n (frame, "", 26);
zframe_print_n (frame, "Prefix", 26);

// mid len
// - "[519] Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin finibus"
//   "[519]  ligula et aliquam tristique. Phasellus consequat, enim et blandit var"
//   "[519] ius, sapie"
// - "Prefix[519] Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin finibus"
//   "Prefix[519]  ligula et aliquam tristique. Phasellus consequat, enim et blandit var"
//   "Prefix[519] ius, sapie"
zframe_print_n (frame, "", 150);
zframe_print_n (frame, "Prefix", 150);

// len > data
//  - emits the whole paragraph
zframe_print_n (frame, "", 1500);
zframe_print_n (frame, "Prefix", 1500);

// max len
//  - emits the whole paragraph
zframe_print_n (frame, "", -1);
zframe_print_n (frame, "Prefix", -1);

zframe_destroy (&frame);

#if defined (__WINDOWS__)
zsys_shutdown();
#endif
and that's the whole test, bestie. no notes, it ate. πŸ‘‘

πŸ¦‹ AUTHORS πŸ¦‹

the czmq manual was written by the authors in the AUTHORS file. that's the whole thing, they're all in there together, group project energy 🀝


🌟 RESOURCES 🌟

main web site:

report bugs to the email <zeromq-dev@lists.zeromq.org[1]>


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.

fr fr read the license, it's giving legal but make it cute πŸ’œ

πŸ“Ž NOTES πŸ“Ž

  1. zeromq-dev@lists.zeromq.org mailto:zeromq-dev@lists.zeromq.org

and that's a wrap on ZFRAME(3), bestie πŸŽ€ this was CZMQ 4.2.1, dated 01/16/2026 β€” go forth and frame responsibly, mwah πŸ’…βœ¨

πŸ“¦ where this page comes from