Message passing with czmq - c

I'm trying to use czmq, the first test was ok with the inproc protocol and if the "puller" and the "pusher" in the same program.
But I want to use it on different processus, I also tried ipc and tcp, and I can not achieve to make communicate the server and the client.
The server:
#include <czmq.h>
int main (void)
{
zctx_t *ctx = zctx_new ();
void *reader = zsocket_new (ctx, ZMQ_PULL);
int rc = zsocket_connect (reader, "tcp://localhost:5555");
printf("wait for a message...\n");
char *message = zstr_recv (reader);
printf("Message: %s",message);
zctx_destroy (&ctx);
return 0;
}
and the client:
#include <czmq.h>
int main (void)
{
zctx_t *ctx = zctx_new ();
void *writer = zsocket_new (ctx, ZMQ_PUSH);
int rc = zsocket_bind (writer, "tcp://*:5555");
assert (rc == service);
zstr_send (writer, "HELLO");
zsocket_destroy (ctx, writer);
return 0;
}
Could you tell me what is wrong with my code. I have also tried other sample codes found, but without more success.
Update
The server is waiting for messages in zstr_recv, but the messages send by the client triggers nothing on the server process.

After sending the message, the client process is destroying the socket too quickly. With inproc, you "get away with it" because inproc is fast, while TCP has to go through more hurdles before the message gets to the TCP stack.
It is true that zsocket_destroy() should block until the message is sent, if ZMQ_LINGER = -1 (the default with raw ZMQ), but the default linger for CZMQ is 0. That means dropping in-transit messages when the socket is destroyed.
Try setting the linger (with zctx_set_linger) to something bigger than zero; 10ms perhaps, but use whatever value is good for you.

Related

libmosquitto does not properly reconnect

I am experimenting with libmosquitto-dev on Raspbian and having some issues.
My code works absolutely fine so far. I can connect to a broker and once the topic gets an update my programm prints the message as it should.
It's just the point when the broker dies after connection and gets restarted.
My code realize the connection dropped and tries to reconnect. Once the broker is back online my code reconnects. But from this on it does not print any updates on the channel.
Why not? I thought this would catch up the connection fine, but it does not.
Her's my code:
[...]
static int run = 1;
void connect_callback(struct mosquitto *mosq, void *obj, int result)
{
printf("connect callback, rc=%d\n", result);
}
void message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message)
{
bool match = 0;
printf("got message '%.*s' for topic '%s'\n", message->payloadlen, (char*) message->payload, message->topic);
mosquitto_topic_matches_sub("Heizung", message->topic, &match);
if (match) {
printf("got message for HEIZUNG topic\n");
}
}
int main(int argc, char *argv[])
{
uint8_t reconnect = true;
char clientid[24];
struct mosquitto *mosq;
int rc = 0;
mosquitto_lib_init();
memset(clientid, 0, 24);
snprintf(clientid, 23, "mylog_%d", getpid());
mosq = mosquitto_new(clientid, true, 0);
if(mosq){
mosquitto_connect_callback_set(mosq, connect_callback);
mosquitto_message_callback_set(mosq, message_callback);
rc = mosquitto_connect(mosq, mqtt_host, mqtt_port, 60);
mosquitto_subscribe(mosq, NULL, "Heizung", 0);
// rc = mosquitto_loop_forever(mosq,20,5); // Tried with this function but same issue.
while(run){
rc = mosquitto_loop(mosq, -1, 1);
if(run && rc){
printf("connection error!\n");
sleep(10);
mosquitto_reconnect(mosq);
}
}
mosquitto_destroy(mosq);
}
mosquitto_lib_cleanup();
return rc;
}
What I see as output is the following:
connect callback, rc=0
got message 'ON1' for topic 'Heizung'
got message for Heizung topic
got message 'ON2' for topic 'Heizung'
got message for Heizung topic
got message 'ON3' for topic 'Heizung'
got message for Heizung topic
connection error!
connect callback, rc=0
You see the connection error (where "systemctl stop mosquitto" took place). And you see reconnection appears to be successful once the broker is back again. But it does not print any of the new messages which are send by the subscriber after the broker is back. Running the mosquitto_sub command in parallel sees all messages!
Any idea what is wrong here?
Thanks a lot!
/KNEBB
Move the call to mosquitto_subscribe to the connect_callback that way it will get called on a reconnect.
Since you are connecting with the CleanSession flag set to true each time you reconnect there will be no persistent session so the broker will not know to keep the subscription.

Network diagnostics for ZeroMQ Example

I am trying to implement ZeroMQ to get an application on a Raspberry Pi 3 (Raspbian Stretch) to communicate with an application on a separate machine (in this case Windows 7 64bit OS) linked by a wired or WLAN connection.
I have compiled ZeroMQ with the C library interface on both machines (using Cygwin on Windows) and the Hello World example (which I modified slightly to print the pointer values to assure me that the functions were 'working'). Both machines are connected (in this case via a wired Ethernet link and a router) and the connection is good (I link to RPi from PC via Xrdp or SSH OK).
The problem I have is that the client/server ZeroMQ programs don't appear to be 'seeing' each other even though they do appear to work and my question is: What are the first steps I should take to investigate why this is happening? Are there any commandline or GUI tools that can help me find out what's causing the blockage? (like port activity monitors or something?).
I know very little about networking so consider me a novice in all things sockety/servicey in your reply. The source code on the RPi (server) is:
// ZeroMQ Test Server
// Compile with
// gcc -o zserver zserver.c -lzmq
#include <zmq.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
int main (void)
{
void *context=NULL,*responder=NULL;
int rc=1;
// Socket to talk to clients
context = zmq_ctx_new ();
printf("Context pointer = %p\n",context);
responder = zmq_socket (context, ZMQ_REP);
printf("Responder pointer = %p\n",responder);
rc = zmq_bind (responder, "tcp://*:5555");
printf("rc = %d\n",rc);
assert (rc == 0);
while (1) {
char buffer [10];
zmq_recv (responder, buffer, 10, 0);
printf ("Received Hello\n");
sleep (1); // Do some 'work'
zmq_send (responder, "World", 5, 0);
}
return 0;
}
The source code on the PC (Cygwin) client is:
// ZeroMQ Test Client
// Compile with:
// gcc -o zclient zclient.c -L/usr/local/lib -lzmq
#include <zmq.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
int main (void)
{
void *context=NULL,*requester=NULL;
printf ("Connecting to hello world server\n");
context = zmq_ctx_new ();
printf("Context pointer = %p\n",context);
requester = zmq_socket (context, ZMQ_REQ);
printf("Requester pointer = %p\n",requester);
zmq_connect (requester, "tcp://localhost:5555");
int request_nbr;
for (request_nbr = 0; request_nbr != 10; request_nbr++) {
char buffer [10];
printf ("Sending Hello %d\n", request_nbr);
zmq_send (requester, "Hello", 5, 0);
zmq_recv (requester, buffer, 10, 0);
printf ("Received World %d\n", request_nbr);
}
zmq_close (requester);
zmq_ctx_destroy (context);
return 0;
}
On the RPi LXTerminal I run the server and get this:
Context pointer = 0xefe308
Responder pointer = 0xf00e08
rc = 0
and on the Cygwin Bash shell I run the client and get this:
Connecting to hello world server
Context pointer = 0x60005ab90
Requester pointer = 0x60005f890
Sending Hello 0
... and there they both hang - one listening, the other sending but neither responding to each other.
Any clue how to start investigating this would be appreciated.
+1 for a care using explicit zmq_close() and zmq_ctx_term() release of resources ...
In case this is the first time to work with ZeroMQ,
one may here enjoy to first look at "ZeroMQ Principles in less than Five Seconds" before diving into further details
Q : What are the first steps I should take to investigate why this is happening?
A Line-of-Sight test as a step zero makes no sense here.
All localhost-placed interfaces are hard to not "see" one another.
Next, test as a first step call { .bind() | .connect() }-methods using an explicit address like tcp://127.0.0.1:56789 ( so as to avoid the expansion of both the *-wildcard and the localhost-symbolic name translations )
Always be ready to read/evaluate the API-provided errno that ZeroMQ keeps reporting about the last ZeroMQ API-operation resultin error-state.
Best read the ZeroMQ native API documentation, which is well maintained from version to version, so as to fully understand the comfort of API designed signaling/messaging meta-plane.
Mea Culpa: the LoS is sure not to have been established by the O/P code:
RPi .bind()-s on it's local I/F ( and cannot otherwise )
PC .connect()-s not to that of RPi, but the PC's local I/F
PC .connect( "tcp://<address_of_RPi>:5555" ) will make it ( use the same IP-address as you use in Xrdp or SSH to connect to RPi or may read one explicitly from RPi CLI-terminal after ~$ ip address and use that one for PC-side client code )
Two disjoint ZeroMQ AccessPoint-s have zero way how to communicate,once no transport-"wire" from A to B
// Zero MQ Test Server
// Compile with
// gcc -o zserver zserver.c -lzmq
#include <zmq.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
int main (void)
{
void *context=NULL,*responder=NULL;
int rc=1;
// Socket to talk to clients
context = zmq_ctx_new (); printf("Context pointer = %p\n",context);
responder = zmq_socket (context, ZMQ_REP); printf("Responder pointer = %p\n",responder);
rc = zmq_bind (responder, "tcp://*:5555"); printf("rc = %d\n",rc);
/* ----------------------------------^^^^^^------------RPi interface-----------*/
assert (rc == 0);
while (1) {
char buffer [10];
zmq_recv (responder, buffer, 10, 0); printf("Received Hello\n");
sleep (1); // Do some 'work'
zmq_send (responder, "World", 5, 0);
}
return 0;
}
The source code on the PC (Cygwin) client is:
// ZeroMQ Test Client
// Compile with:
// gcc -o zclient zclient.c -L/usr/local/lib -lzmq
#include <zmq.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
int main (void)
{
void *context=NULL,*requester=NULL;
printf("Connecting to hello world server\n");
context = zmq_ctx_new (); printf("Context pointer = %p\n",context);
requester = zmq_socket (context, ZMQ_REQ); printf("Requester pointer = %p\n",requester);
zmq_connect (requester, "tcp://localhost:5555");
/*---------------------------------^^^^^^^^^^^^^^---------PC-local-interface------*/
int request_nbr;
for (request_nbr = 0; request_nbr != 10; request_nbr++) {
char buffer [10]; printf("Sending Hello %d\n", request_nbr);
zmq_send (requester, "Hello", 5, 0);
zmq_recv (requester, buffer, 10, 0); printf("Received World %d\n", request_nbr);
}
zmq_close (requester);
zmq_ctx_destroy (context);
return 0;
}
May like to also read more on ZeroMQ-related subjects here
Epilogue :
The trouble reported in the O/P is actually masked and remains hidden from being detectable by the API. ZeroMQ permits one AccessPoint to have 0+ transport-class-connections simultaneously, given a proper syntax and other conditions are met.
A call tozmq_connect( reguester, "tcp://<address-not-intended-but-correct>:<legal-port>" ) will result in legally-fair state and none of the defined and documented cases of possible error-states would get reported, because none of all such cases did actually happen:
EINVAL
The endpoint supplied is invalid.
EPROTONOSUPPORT
The requested transport protocol is not supported.
ENOCOMPATPROTO
The requested transport protocol is not compatible with the socket type.
ETERM
The ØMQ context associated with the specified socket was terminated.
ENOTSOCK
The provided socket was invalid.
EMTHREAD
No I/O thread is available to accomplish the task.
There are some chances to at least somehow-"detect" the trouble would be to enforce another sort of exception/error, but deferred into the call of { zmq_recv() | zmq_recv() } in their non-blocking form, where these may turn into reporting EAGAIN or might be EFSM for not having completed the end-to-end re-confirmed ZMTP-protocol handshaking ( no counterparty was and would never be met on the PC-localhost-port with remote RPi-server-side ). This requires also prior settings of zmq_setsockopt( responder, ZMQ_IMMEDIATE, 1 ) and other configuration details.
Next one, in ZeroMQ v4.+, there is a chance to inspect a subset of AccessPoint's internally reported events, using an "inspection-socket" via a rather complex strategy of instantiatingint zmq_socket_monitor (void *socket, char *endpoint, int events); attached to the AccessPoint's internals via inproc:// transport-class ~ here "inproc://myPCsocketAccessPOINT_monitor" like this:
rc = zmq_socket_monitor( responder, // AccessPoint to monitor
"inproc://myPCsocketAccessPOINT_monitor", // symbolinc name
ZMQ_ALL_EVENTS // scope of Events
);
Such created internal monitoring "inspection-socket" may next get zmq_connect()-ed to like:
void *my_end_of_monitor_socket = zmq_socket ( context, ZMQ_PAIR );
rc = zmq_connect( my_end_of_monitor_socket, // local-end PAIR-socket AccessPoint
"inproc://myPCsocketAccessPOINT_monitor" // symbolic name
);
and finally, we can use this to read a sequence of events (and act accordingly ):
int event = get_monitor_event( my_end_of_monitor_socket, NULL, NULL );
if (event == ZMQ_EVENT_CONNECT_DELAYED) { ...; }
if (event == ... ) { ...; }
using as a tool a trivialised get_monitor_event() like this, that handles some of the internal rules of reading and interpreting the multi-part messages that come as ordered from the instantiated "internal"-monitor attached to the AccessPoint:
// Read one event off the monitor socket; return value and address
// by reference, if not null, and event number by value. Returns -1
// in case of error.
static int
get_monitor_event ( void *monitor, int *value, char **address )
{
// First frame in message contains event number and value
zmq_msg_t msg;
zmq_msg_init (&msg);
if (zmq_msg_recv (&msg, monitor, 0) == -1) return -1; // Interrupted, presumably
assert (zmq_msg_more (&msg));
uint8_t *data = (uint8_t *) zmq_msg_data (&msg);
uint16_t event = *(uint16_t *) (data);
if (value) *value = *(uint32_t *) (data + 2);
// Second frame in message contains event address
zmq_msg_init (&msg);
if (zmq_msg_recv (&msg, monitor, 0) == -1) return -1; // Interrupted, presumably
assert (!zmq_msg_more (&msg));
if (address) {
uint8_t *data = (uint8_t *) zmq_msg_data (&msg);
size_t size = zmq_msg_size (&msg);
*address = (char *) malloc (size + 1);
memcpy (*address, data, size);
(*address)[size] = 0;
}
return event;
}
What internal-API-events can be monitored ?
As of the state of v4.2 API, there is this set of "internal"-monitor(able) internal-API-events:
ZMQ_EVENT_CONNECTED
The socket has successfully connected to a remote peer. The event value is the file descriptor (FD) of the underlying network socket. Warning: there is no guarantee that the FD is still valid by the time your code receives this event.
ZMQ_EVENT_CONNECT_DELAYED
A connect request on the socket is pending. The event value is unspecified.
ZMQ_EVENT_CONNECT_RETRIED
A connect request failed, and is now being retried. The event value is the reconnect interval in milliseconds. Note that the reconnect interval is recalculated at each retry.
ZMQ_EVENT_LISTENING
The socket was successfully bound to a network interface. The event value is the FD of the underlying network socket. Warning: there is no guarantee that the FD is still valid by the time your code receives this event.
ZMQ_EVENT_BIND_FAILED
The socket could not bind to a given interface. The event value is the errno generated by the system bind call.
ZMQ_EVENT_ACCEPTED
The socket has accepted a connection from a remote peer. The event value is the FD of the underlying network socket. Warning: there is no guarantee that the FD is still valid by the time your code receives this event.
ZMQ_EVENT_ACCEPT_FAILED
The socket has rejected a connection from a remote peer. The event value is the errno generated by the accept call.
ZMQ_EVENT_CLOSED
The socket was closed. The event value is the FD of the (now closed) network socket.
ZMQ_EVENT_CLOSE_FAILED
The socket close failed. The event value is the errno returned by the system call. Note that this event occurs only on IPC transports.
ZMQ_EVENT_DISCONNECTED
The socket was disconnected unexpectedly. The event value is the FD of the underlying network socket. Warning: this socket will be closed.
ZMQ_EVENT_MONITOR_STOPPED
Monitoring on this socket ended.
ZMQ_EVENT_HANDSHAKE_FAILED
The ZMTP security mechanism handshake failed. The event value is unspecified.
NOTE: in DRAFT state, not yet available in stable releases.
ZMQ_EVENT_HANDSHAKE_SUCCEED
NOTE: as new events are added, the catch-all value will start returning them. An application that relies on a strict and fixed sequence of events must not use ZMQ_EVENT_ALL in order to guarantee compatibility with future versions.
Each event is sent as two frames. The first frame contains an event number (16 bits), and an event value (32 bits) that provides additional data according to the event number. The second frame contains a string that specifies the affected TCP or IPC endpoint.
In zmq_connect, you must indicate the IP address of the raspberry (which have executed zmq_bind:
It should have been:
// on PC, remote ip is the raspberry one, the one you use for ssh for instance
rc = zmq_connect(requester, "tcp://<remote ip>:5555");

Node.js ZeroMQ client does not exchange REQ/REP messages with a C ZeroMQ server

Using the examples provided by the ZeroMQ docs, I cannot get them work with a server written in C and a node.js client.
The examples I use are:
http://zguide.zeromq.org/js:rrclient
for Node.js:
// Hello World client in Node.js
// Connects REQ socket to tcp://localhost:5559
// Sends "Hello" to server, expects "World" back
var zmq = require('zmq')
, requester = zmq.socket('req');
requester.connect('tcp://localhost:5560');
var replyNbr = 0;
requester.on('message', function(msg) {
console.log('got reply', replyNbr, msg.toString());
replyNbr += 1;
});
for (var i = 0; i < 10; ++i) {
requester.send("Hello");
}
and
https://github.com/booksbyus/zguide/blob/master/examples/C/rrworker.c
for the C server:
// Hello World worker
// Connects REP socket to tcp://localhost:5560
// Expects "Hello" from client, replies with "World"
#include "zhelpers.h"
#include <unistd.h>
int main (void)
{
void *context = zmq_ctx_new ();
// Socket to talk to clients
void *responder = zmq_socket (context, ZMQ_REP);
//zmq_connect (responder, "tcp://localhost:5560");
// using bind instead of connect
zmq_bind (responder, "tcp://localhost:5560");
while (1) {
// Wait for next request from client
char *string = s_recv (responder);
printf ("Received request: [%s]\n", string);
free (string);
// Do some 'work'
sleep (1);
// Send reply back to client
s_send (responder, "World");
}
// We never get here, but clean up anyhow
zmq_close (responder);
zmq_ctx_destroy (context);
return 0;
}
I changed the port, so they now match ( 5560 ). However I get no data transmitted. Neither the client nor the server gets any message.
Why? Simply they both just remained listening
Where?
requester.connect(..) // in Node.js copy/paste code
resp.
zmq_connect ( responder, "tcp://localhost:5560" ); // in C copy/paste code
The logic of a ZeroMQ signalling / messaging infrastructure is a bit more complex.
One side of the REQ/REP has to .bind() and all the others may try to .connect().
This is valid in principle, applicable to all ZeroMQ Scalable Formal Communication Pattern archetypes, not just to the REQ/REP one.
So, in this use-case,
either side -- be it a Node.js or the C -- may start with the .bind()
and
the other one will be able to try to .connect() to such a .bind()-prepared and ready IP:port# target.
..
int rc = zmq_bind( responder, "tcp://localhost:5560" );
/* zmq_bind()
returns:
* zero if successful.
* -1 otherwise
and
sets errno to one of the values
as defined in API.
*/
..
There are many good practices to follow in ZeroMQ domain. Registering and handling the return codes from the function calls being one such topic in ZeroMQ best practices. Do not hesitate to learn faster and read through many man*years of a Collective-Experience in this here.
Binding to localhost does not seem to work, so I tried 127.0.0.1 and it works.

C ZeroMQ zstr_recv hangs

I have two simple programs: a client and a server. I'm trying to use zstr_sendfm and zstr_recv to send and receive a simple string. Roughly speaking, I'm using the code from the file transfer test in the zeromq tutorial. Here's the server function:
#define PIPELINE = 10;
int server()
{
char *name = "someName";
zctx_t *ctx = zctx_new();
void *router = zsocket_new(ctx, ZMQ_ROUTER);
zsocket_set_hwm(router, PIPELINE*2);
if (0 == zsocket_connect(router, tcp://127.0.0.1:6000))
{
printf("failed to connect to router.\n");
}
printf( "sending name %s\n, name);
zstr_sendfm( router, name );
return 0;
}
Here's the client function:
int client()
{
zctx_t *ctx = zctx_new ();
void *dealer = zsocket_new (ctx, ZMQ_DEALER);
zsocket_bind(dealer, "tcp://*:6000")
char *receivedName = zstr_recv( dealer );
printf("received the following name: %s\n", receivedName);
return 0
}
Both of these are run in two separate programs (which do nothing other than run their respective functions) on the same computer.
Here's how things always play out:
Start client function, which holds at "zstr_recv" as it's supposed to
Start server function, which connects successfully, claims to have sent the data, and exits
Client function continues to sit and wait, but claims to have not received anything from the server.
What am I missing here? I've added a bunch of error checking and even tried this out in gdb with no luck.
Help and advice appreciated.
I think you have your client and server mixed up, although in ZeroMQ client and server is not as strict as with normal sockets. Normally you would create a server with a REP socket that binds/receives/sends and a client with a REQ socket that connects/sends/receives. You should try this first and then experiment with ROUTER for the server (instead of REP) and DEALER for the client (instead of REQ).

Sending structs with ZeroMQ and ProtocolBuffers

I'm writing a program that's supposed to send C structures via ZeroMQ.
Therefore I'm using Google's ProtocolBuffers to serialize the structs.
I do now have the problem that my subscriber side is not receiving anything.
The Publisher prints out "Message successfully sent" so I think the Error occurs on the Subscribers side.
Publisher:
int main (void)
{
Message protomsg = MESSAGE__INIT;
void *buf;
unsigned len;
void *context = zmq_ctx_new();
void *subscriber = zmq_socket(context, ZMQ_PUB);
zmq_bind(subscriber, "ipc://my.sock");
//Initialising protomsg (not so important)
//sending message
len = message__get_packed_size(&protomsg);
buf = malloc(len);
message__pack(&protomsg, buf);
zmq_msg_t output;
zmq_msg_init_size(&output, len);
zmq_msg_init_data(&output, buf, len, NULL, NULL);
if(zmq_msg_send(&output, subscriber, 0) == -1)
perror("Error sending message \n");
else
printf("Message successfully sent \n");
zmq_msg_close(&output);
free(buf);
zmq_close (subscriber);
zmq_ctx_destroy (context);
return 0;
}
Subscriber:
int main (void){
Message *protomsg;
void *context = zmq_ctx_new ();
void *publisher = zmq_socket (context, ZMQ_SUB);
zmq_connect(publisher, "ipc://my.sock");
zmq_setsockopt(publisher, ZMQ_SUBSCRIBE, "", 0);
// Read packed message from ZMQ.
zmq_msg_t msg;
zmq_msg_init(&msg);
if(zmq_msg_recv(&msg, publisher, 0) == -1)
perror("Error receiving message \n");
else
printf("Message received");
memcpy((void *)protomsg, zmq_msg_data(&msg), zmq_msg_size(&msg));
// Unpack the message using protobuf-c.
protomsg = message__unpack(NULL, zmq_msg_size(&msg), (void *)&data);
if (protomsg == NULL)
{
fprintf(stderr, "error unpacking incoming message\n");
exit(1);
}
printf("Address: %u, Type: %u, Information[0]: %u, Information[1]: %u \n", protomsg->address-48, protomsg->frametype, protomsg->information[0], protomsg->information[1]);
zmq_msg_close (&msg);
// Free the unpacked message
message__free_unpacked(protomsg, NULL);
//close context,socket..
}
Don't know if anyone still cares about this, but here goes... I agree with #Steve-o that this is a timing issue, although I think the problem is that you are closing the publisher socket too soon.
Your publisher code publishes the message then immediately closes the socket and terminates the context. So the message exists in the publisher for milliseconds and then is gone forever.
If you run the publisher first, it does it's thing, exits and the message is gone. When you start the subscriber it attempts to connect to an IPC socket that is no longer there. ZeroMQ allows this and the subscriber will block until there is an IPC socket to connect to.
I have not reviewed the ZeroMQ IPC source code, but I suspect that, under the covers, subscriber is periodically attempting to connect to the publisher socket. Now if you run the publisher again, it might work but you have a serious race condition. If you start the publisher at the exact instant the ZeroMQ worker was attempting to retry, the connect might happen and you might even get your message before the publisher destroys everything.
I am pretty sure the problem has nothing to do with structs and protobuf. From the ZeroMQ point of view you are just sending bytes. There is no difference. If your test cases for ZeroMQ strings were truly identical with the test cases for ZeroMQ structs - then perhaps the code change added or removed a few nano-seconds that was able to break the race condition the wrong way.
Specific suggestions:
rename the socket in publisher to be "publisher" instead of subscriber (copy/paste error)
add a sleep for 30 seconds just before zmq_close (publisher);
hopefully this will fix the problem for your test code
if this does not fix it, consider switching to tcp transport and use wireshark to diagnose what is really going on.

Resources