zmsg_t * zgossip_msg_encode_invalid ( ) { zgossip_msg_t *self = zgossip_msg_new (ZGOSSIP_MSG_INVALID); return zgossip_msg_encode (&self); }
zgossip_msg_t * zgossip_msg_decode (zmsg_t **msg_p) { assert (msg_p); zmsg_t *msg = *msg_p; if (msg == NULL) return NULL; zgossip_msg_t *self = zgossip_msg_new (0); // Read and parse command in frame zframe_t *frame = zmsg_pop (msg); if (!frame) goto empty; // Malformed or empty // Get and check protocol signature self->needle = zframe_data (frame); self->ceiling = self->needle + zframe_size (frame); uint16_t signature; GET_NUMBER2 (signature); if (signature != (0xAAA0 | 0)) goto empty; // Invalid signature // Get message id and parse per message type GET_NUMBER1 (self->id); switch (self->id) { case ZGOSSIP_MSG_HELLO: break; case ZGOSSIP_MSG_ANNOUNCE: GET_STRING (self->endpoint); GET_STRING (self->service); break; case ZGOSSIP_MSG_PING: break; case ZGOSSIP_MSG_PONG: break; case ZGOSSIP_MSG_INVALID: break; default: goto malformed; } // Successful return zframe_destroy (&frame); zmsg_destroy (msg_p); return self; // Error returns malformed: printf ("E: malformed message '%d'\n", self->id); empty: zframe_destroy (&frame); zmsg_destroy (msg_p); zgossip_msg_destroy (&self); return (NULL); }
int zgossip_msg_send_pong ( void *output) { zgossip_msg_t *self = zgossip_msg_new (ZGOSSIP_MSG_PONG); return zgossip_msg_send (&self, output); }
zgossip_msg_t * zgossip_msg_dup (zgossip_msg_t *self) { if (!self) return NULL; zgossip_msg_t *copy = zgossip_msg_new (self->id); if (self->routing_id) copy->routing_id = zframe_dup (self->routing_id); switch (self->id) { case ZGOSSIP_MSG_HELLO: break; case ZGOSSIP_MSG_ANNOUNCE: copy->endpoint = self->endpoint? strdup (self->endpoint): NULL; copy->service = self->service? strdup (self->service): NULL; break; case ZGOSSIP_MSG_PING: break; case ZGOSSIP_MSG_PONG: break; case ZGOSSIP_MSG_INVALID: break; } return copy; }
zmsg_t * zgossip_msg_encode_pong ( ) { zgossip_msg_t *self = zgossip_msg_new (ZGOSSIP_MSG_PONG); return zgossip_msg_encode (&self); }
zmsg_t * zgossip_msg_encode_hello ( ) { zgossip_msg_t *self = zgossip_msg_new (ZGOSSIP_MSG_HELLO); return zgossip_msg_encode (&self); }
int zgossip_msg_send_invalid ( void *output) { zgossip_msg_t *self = zgossip_msg_new (ZGOSSIP_MSG_INVALID); return zgossip_msg_send (&self, output); }
zgossip_msg_t * zgossip_msg_dup (zgossip_msg_t *self) { if (!self) return NULL; zgossip_msg_t *copy = zgossip_msg_new (self->id); if (self->routing_id) copy->routing_id = zframe_dup (self->routing_id); switch (self->id) { case ZGOSSIP_MSG_HELLO: copy->version = self->version; break; case ZGOSSIP_MSG_PUBLISH: copy->version = self->version; copy->key = self->key? strdup (self->key): NULL; copy->value = self->value? strdup (self->value): NULL; break; case ZGOSSIP_MSG_PING: copy->version = self->version; break; case ZGOSSIP_MSG_PONG: copy->version = self->version; break; case ZGOSSIP_MSG_INVALID: copy->version = self->version; break; } return copy; }
static void server_connect (server_t *self, const char *endpoint) { zsock_t *remote = zsock_new (ZMQ_DEALER); assert (remote); // No recovery if exhausted // Never block on sending; we use an infinite HWM and buffer as many // messages as needed in outgoing pipes. Note that the maximum number // is the overall tuple set size. zsock_set_unbounded (remote); if (zsock_connect (remote, "%s", endpoint)) { zsys_warning ("bad zgossip endpoint '%s'", endpoint); zsock_destroy (&remote); return; } // Send HELLO and then PUBLISH for each tuple we have zgossip_msg_t *gossip = zgossip_msg_new (); zgossip_msg_set_id (gossip, ZGOSSIP_MSG_HELLO); zgossip_msg_send (gossip, remote); tuple_t *tuple = (tuple_t *) zhashx_first (self->tuples); while (tuple) { zgossip_msg_set_id (gossip, ZGOSSIP_MSG_PUBLISH); zgossip_msg_set_key (gossip, tuple->key); zgossip_msg_set_value (gossip, tuple->value); zgossip_msg_send (gossip, remote); tuple = (tuple_t *) zhashx_next (self->tuples); } // Now monitor this remote for incoming messages zgossip_msg_destroy (&gossip); engine_handle_socket (self, remote, remote_handler); zlistx_add_end (self->remotes, remote); }
int zgossip_msg_send_hello ( void *output) { zgossip_msg_t *self = zgossip_msg_new (ZGOSSIP_MSG_HELLO); return zgossip_msg_send (&self, output); }
zmsg_t * zgossip_msg_encode_announce ( const char *endpoint, const char *service) { zgossip_msg_t *self = zgossip_msg_new (ZGOSSIP_MSG_ANNOUNCE); zgossip_msg_set_endpoint (self, endpoint); zgossip_msg_set_service (self, service); return zgossip_msg_encode (&self); }
zmsg_t * zgossip_msg_encode_publish ( const char *key, const char *value) { zgossip_msg_t *self = zgossip_msg_new (ZGOSSIP_MSG_PUBLISH); zgossip_msg_set_key (self, key); zgossip_msg_set_value (self, value); return zgossip_msg_encode (&self); }
int zgossip_msg_send_publish ( void *output, const char *key, const char *value) { zgossip_msg_t *self = zgossip_msg_new (ZGOSSIP_MSG_PUBLISH); zgossip_msg_set_key (self, key); zgossip_msg_set_value (self, value); return zgossip_msg_send (&self, output); }
int zgossip_msg_send_announce ( void *output, const char *endpoint, const char *service) { zgossip_msg_t *self = zgossip_msg_new (ZGOSSIP_MSG_ANNOUNCE); zgossip_msg_set_endpoint (self, endpoint); zgossip_msg_set_service (self, service); return zgossip_msg_send (&self, output); }
server_connect (server_t *self, const char *endpoint) #endif { zsock_t *remote = zsock_new (ZMQ_DEALER); assert (remote); // No recovery if exhausted #ifdef CZMQ_BUILD_DRAFT_API // DRAFT-API: Security if (public_key){ zcert_t *cert = zcert_new_from_txt (self->public_key, self->secret_key); zcert_apply(cert, remote); zsock_set_curve_serverkey (remote, public_key); #ifndef ZMQ_CURVE // legacy ZMQ support // inline incase the underlying assert is removed bool ZMQ_CURVE = false; #endif assert (zsock_mechanism (remote) == ZMQ_CURVE); zcert_destroy(&cert); } #endif // Never block on sending; we use an infinite HWM and buffer as many // messages as needed in outgoing pipes. Note that the maximum number // is the overall tuple set size. zsock_set_unbounded (remote); if (zsock_connect (remote, "%s", endpoint)) { zsys_warning ("bad zgossip endpoint '%s'", endpoint); zsock_destroy (&remote); return; } // Send HELLO and then PUBLISH for each tuple we have zgossip_msg_t *gossip = zgossip_msg_new (); zgossip_msg_set_id (gossip, ZGOSSIP_MSG_HELLO); zgossip_msg_send (gossip, remote); tuple_t *tuple = (tuple_t *) zhashx_first (self->tuples); while (tuple) { zgossip_msg_set_id (gossip, ZGOSSIP_MSG_PUBLISH); zgossip_msg_set_key (gossip, tuple->key); zgossip_msg_set_value (gossip, tuple->value); zgossip_msg_send (gossip, remote); tuple = (tuple_t *) zhashx_next (self->tuples); } // Now monitor this remote for incoming messages zgossip_msg_destroy (&gossip); engine_handle_socket (self, remote, remote_handler); zlistx_add_end (self->remotes, remote); }
static int server_initialize (server_t *self) { // Default timeout for clients is one second; the caller can // override this with a SET message. engine_configure (self, "server/timeout", "1000"); self->message = zgossip_msg_new (); self->remotes = zlistx_new (); assert (self->remotes); zlistx_set_destructor (self->remotes, (czmq_destructor *) zsock_destroy_); self->tuples = zhashx_new (); assert (self->tuples); return 0; }
static int server_initialize (server_t *self) { // Default timeout for clients is one second; the caller can // override this with a SET message. engine_configure (self, "server/timeout", "1000"); self->message = zgossip_msg_new (); self->remotes = zlistx_new (); assert (self->remotes); zlistx_set_destructor (self->remotes, (czmq_destructor *) zsock_destroy); self->tuples = zhashx_new (); assert (self->tuples); #ifdef CZMQ_BUILD_DRAFT_API self->zap_domain = strdup(CZMQ_ZGOSSIP_ZAP_DOMAIN); #endif return 0; }
static void server_accept (server_t *self, const char *key, const char *value) { tuple_t *tuple = (tuple_t *) zhashx_lookup (self->tuples, key); if (tuple && streq (tuple->value, value)) return; // Duplicate tuple, do nothing // Create new tuple tuple = (tuple_t *) zmalloc (sizeof (tuple_t)); assert (tuple); tuple->container = self->tuples; tuple->key = strdup (key); tuple->value = strdup (value); // Store new tuple zhashx_update (tuple->container, key, tuple); zhashx_freefn (tuple->container, key, tuple_free); // Deliver to calling application zstr_sendx (self->pipe, "DELIVER", key, value, NULL); // Hold in server context so we can broadcast to all clients self->cur_tuple = tuple; engine_broadcast_event (self, NULL, forward_event); // Copy new tuple announcement to all remotes zgossip_msg_t *gossip = zgossip_msg_new (); zgossip_msg_set_id (gossip, ZGOSSIP_MSG_PUBLISH); zsock_t *remote = (zsock_t *) zlistx_first (self->remotes); while (remote) { zgossip_msg_set_key (gossip, tuple->key); zgossip_msg_set_value (gossip, tuple->value); zgossip_msg_send (gossip, remote); remote = (zsock_t *) zlistx_next (self->remotes); } zgossip_msg_destroy (&gossip); }
void zgossip_test (bool verbose) { printf (" * zgossip: "); if (verbose) printf ("\n"); // @selftest // Test basic client-to-server operation of the protocol zactor_t *server = zactor_new (zgossip, "server"); assert (server); if (verbose) zstr_send (server, "VERBOSE"); zstr_sendx (server, "BIND", "inproc://zgossip", NULL); zsock_t *client = zsock_new (ZMQ_DEALER); assert (client); zsock_set_rcvtimeo (client, 2000); int rc = zsock_connect (client, "inproc://zgossip"); assert (rc == 0); // Send HELLO, which gets no message zgossip_msg_t *message = zgossip_msg_new (); zgossip_msg_set_id (message, ZGOSSIP_MSG_HELLO); zgossip_msg_send (message, client); // Send PING, expect PONG back zgossip_msg_set_id (message, ZGOSSIP_MSG_PING); zgossip_msg_send (message, client); zgossip_msg_recv (message, client); assert (zgossip_msg_id (message) == ZGOSSIP_MSG_PONG); zgossip_msg_destroy (&message); zactor_destroy (&server); zsock_destroy (&client); // Test peer-to-peer operations zactor_t *base = zactor_new (zgossip, "base"); assert (base); if (verbose) zstr_send (base, "VERBOSE"); // Set a 100msec timeout on clients so we can test expiry zstr_sendx (base, "SET", "server/timeout", "100", NULL); zstr_sendx (base, "BIND", "inproc://base", NULL); zactor_t *alpha = zactor_new (zgossip, "alpha"); assert (alpha); zstr_sendx (alpha, "CONNECT", "inproc://base", NULL); zstr_sendx (alpha, "PUBLISH", "inproc://alpha-1", "service1", NULL); zstr_sendx (alpha, "PUBLISH", "inproc://alpha-2", "service2", NULL); zactor_t *beta = zactor_new (zgossip, "beta"); assert (beta); zstr_sendx (beta, "CONNECT", "inproc://base", NULL); zstr_sendx (beta, "PUBLISH", "inproc://beta-1", "service1", NULL); zstr_sendx (beta, "PUBLISH", "inproc://beta-2", "service2", NULL); // got nothing zclock_sleep (200); zactor_destroy (&base); zactor_destroy (&alpha); zactor_destroy (&beta); // @end printf ("OK\n"); }
int zgossip_msg_test (bool verbose) { printf (" * zgossip_msg: "); // @selftest // Simple create/destroy test zgossip_msg_t *self = zgossip_msg_new (0); assert (self); zgossip_msg_destroy (&self); // Create pair of sockets we can send through zsock_t *input = zsock_new (ZMQ_ROUTER); assert (input); zsock_connect (input, "inproc://selftest-zgossip_msg"); zsock_t *output = zsock_new (ZMQ_DEALER); assert (output); zsock_bind (output, "inproc://selftest-zgossip_msg"); // Encode/send/decode and verify each message type int instance; zgossip_msg_t *copy; self = zgossip_msg_new (ZGOSSIP_MSG_HELLO); // Check that _dup works on empty message copy = zgossip_msg_dup (self); assert (copy); zgossip_msg_destroy (©); // Send twice from same object zgossip_msg_send_again (self, output); zgossip_msg_send (&self, output); for (instance = 0; instance < 2; instance++) { self = zgossip_msg_recv (input); assert (self); assert (zgossip_msg_routing_id (self)); zgossip_msg_destroy (&self); } self = zgossip_msg_new (ZGOSSIP_MSG_PUBLISH); // Check that _dup works on empty message copy = zgossip_msg_dup (self); assert (copy); zgossip_msg_destroy (©); zgossip_msg_set_key (self, "Life is short but Now lasts for ever"); zgossip_msg_set_value (self, "Life is short but Now lasts for ever"); // Send twice from same object zgossip_msg_send_again (self, output); zgossip_msg_send (&self, output); for (instance = 0; instance < 2; instance++) { self = zgossip_msg_recv (input); assert (self); assert (zgossip_msg_routing_id (self)); assert (streq (zgossip_msg_key (self), "Life is short but Now lasts for ever")); assert (streq (zgossip_msg_value (self), "Life is short but Now lasts for ever")); zgossip_msg_destroy (&self); } self = zgossip_msg_new (ZGOSSIP_MSG_PING); // Check that _dup works on empty message copy = zgossip_msg_dup (self); assert (copy); zgossip_msg_destroy (©); // Send twice from same object zgossip_msg_send_again (self, output); zgossip_msg_send (&self, output); for (instance = 0; instance < 2; instance++) { self = zgossip_msg_recv (input); assert (self); assert (zgossip_msg_routing_id (self)); zgossip_msg_destroy (&self); } self = zgossip_msg_new (ZGOSSIP_MSG_PONG); // Check that _dup works on empty message copy = zgossip_msg_dup (self); assert (copy); zgossip_msg_destroy (©); // Send twice from same object zgossip_msg_send_again (self, output); zgossip_msg_send (&self, output); for (instance = 0; instance < 2; instance++) { self = zgossip_msg_recv (input); assert (self); assert (zgossip_msg_routing_id (self)); zgossip_msg_destroy (&self); } self = zgossip_msg_new (ZGOSSIP_MSG_INVALID); // Check that _dup works on empty message copy = zgossip_msg_dup (self); assert (copy); zgossip_msg_destroy (©); // Send twice from same object zgossip_msg_send_again (self, output); zgossip_msg_send (&self, output); for (instance = 0; instance < 2; instance++) { self = zgossip_msg_recv (input); assert (self); assert (zgossip_msg_routing_id (self)); zgossip_msg_destroy (&self); } zsock_destroy (&input); zsock_destroy (&output); // @end printf ("OK\n"); return 0; }
void zgossip_test (bool verbose) { printf (" * zgossip: "); if (verbose) printf ("\n"); // @selftest // Test basic client-to-server operation of the protocol zactor_t *server = zactor_new (zgossip, "server"); assert (server); if (verbose) zstr_send (server, "VERBOSE"); zstr_sendx (server, "BIND", "inproc://zgossip", NULL); zsock_t *client = zsock_new (ZMQ_DEALER); assert (client); zsock_set_rcvtimeo (client, 2000); int rc = zsock_connect (client, "inproc://zgossip"); assert (rc == 0); // Send HELLO, which gets no message zgossip_msg_t *message = zgossip_msg_new (); zgossip_msg_set_id (message, ZGOSSIP_MSG_HELLO); zgossip_msg_send (message, client); // Send PING, expect PONG back zgossip_msg_set_id (message, ZGOSSIP_MSG_PING); zgossip_msg_send (message, client); zgossip_msg_recv (message, client); assert (zgossip_msg_id (message) == ZGOSSIP_MSG_PONG); zgossip_msg_destroy (&message); zactor_destroy (&server); zsock_destroy (&client); // Test peer-to-peer operations zactor_t *base = zactor_new (zgossip, "base"); assert (base); if (verbose) zstr_send (base, "VERBOSE"); // Set a 100msec timeout on clients so we can test expiry zstr_sendx (base, "SET", "server/timeout", "100", NULL); zstr_sendx (base, "BIND", "inproc://base", NULL); zactor_t *alpha = zactor_new (zgossip, "alpha"); assert (alpha); zstr_sendx (alpha, "CONNECT", "inproc://base", NULL); zstr_sendx (alpha, "PUBLISH", "inproc://alpha-1", "service1", NULL); zstr_sendx (alpha, "PUBLISH", "inproc://alpha-2", "service2", NULL); zactor_t *beta = zactor_new (zgossip, "beta"); assert (beta); zstr_sendx (beta, "CONNECT", "inproc://base", NULL); zstr_sendx (beta, "PUBLISH", "inproc://beta-1", "service1", NULL); zstr_sendx (beta, "PUBLISH", "inproc://beta-2", "service2", NULL); // got nothing zclock_sleep (200); zstr_send (alpha, "STATUS"); char *command, *status, *key, *value; zstr_recvx (alpha, &command, &key, &value, NULL); assert (streq (command, "DELIVER")); assert (streq (key, "inproc://alpha-1")); assert (streq (value, "service1")); zstr_free (&command); zstr_free (&key); zstr_free (&value); zstr_recvx (alpha, &command, &key, &value, NULL); assert (streq (command, "DELIVER")); assert (streq (key, "inproc://alpha-2")); assert (streq (value, "service2")); zstr_free (&command); zstr_free (&key); zstr_free (&value); zstr_recvx (alpha, &command, &key, &value, NULL); assert (streq (command, "DELIVER")); assert (streq (key, "inproc://beta-1")); assert (streq (value, "service1")); zstr_free (&command); zstr_free (&key); zstr_free (&value); zstr_recvx (alpha, &command, &key, &value, NULL); assert (streq (command, "DELIVER")); assert (streq (key, "inproc://beta-2")); assert (streq (value, "service2")); zstr_free (&command); zstr_free (&key); zstr_free (&value); zstr_recvx (alpha, &command, &status, NULL); assert (streq (command, "STATUS")); assert (atoi (status) == 4); zstr_free (&command); zstr_free (&status); zactor_destroy (&base); zactor_destroy (&alpha); zactor_destroy (&beta); #ifdef CZMQ_BUILD_DRAFT_API // DRAFT-API: Security // curve if (zsys_has_curve()) { if (verbose) printf("testing CURVE support"); zclock_sleep (2000); zactor_t *auth = zactor_new(zauth, NULL); assert (auth); if (verbose) { zstr_sendx (auth, "VERBOSE", NULL); zsock_wait (auth); } zstr_sendx(auth,"ALLOW","127.0.0.1",NULL); zsock_wait(auth); zstr_sendx (auth, "CURVE", CURVE_ALLOW_ANY, NULL); zsock_wait (auth); server = zactor_new (zgossip, "server"); if (verbose) zstr_send (server, "VERBOSE"); assert (server); zcert_t *client1_cert = zcert_new (); zcert_t *server_cert = zcert_new (); zstr_sendx (server, "SET PUBLICKEY", zcert_public_txt (server_cert), NULL); zstr_sendx (server, "SET SECRETKEY", zcert_secret_txt (server_cert), NULL); zstr_sendx (server, "ZAP DOMAIN", "TEST", NULL); zstr_sendx (server, "BIND", "tcp://127.0.0.1:*", NULL); zstr_sendx (server, "PORT", NULL); zstr_recvx (server, &command, &value, NULL); assert (streq (command, "PORT")); int port = atoi (value); zstr_free (&command); zstr_free (&value); char endpoint [32]; sprintf (endpoint, "tcp://127.0.0.1:%d", port); zactor_t *client1 = zactor_new (zgossip, "client"); if (verbose) zstr_send (client1, "VERBOSE"); assert (client1); zstr_sendx (client1, "SET PUBLICKEY", zcert_public_txt (client1_cert), NULL); zstr_sendx (client1, "SET SECRETKEY", zcert_secret_txt (client1_cert), NULL); zstr_sendx (client1, "ZAP DOMAIN", "TEST", NULL); const char *public_txt = zcert_public_txt (server_cert); zstr_sendx (client1, "CONNECT", endpoint, public_txt, NULL); zstr_sendx (client1, "PUBLISH", "tcp://127.0.0.1:9001", "service1", NULL); zclock_sleep (500); zstr_send (server, "STATUS"); zclock_sleep (500); zstr_recvx (server, &command, &key, &value, NULL); assert (streq (command, "DELIVER")); assert (streq (value, "service1")); zstr_free (&command); zstr_free (&key); zstr_free (&value); zstr_sendx (client1, "$TERM", NULL); zstr_sendx (server, "$TERM", NULL); zclock_sleep(500); zcert_destroy (&client1_cert); zcert_destroy (&server_cert); zactor_destroy (&client1); zactor_destroy (&server); zactor_destroy (&auth); } #endif #if defined (__WINDOWS__) zsys_shutdown(); #endif // @end printf ("OK\n"); }
void zgossip_msg_test (bool verbose) { printf (" * zgossip_msg:"); if (verbose) printf ("\n"); // @selftest // Simple create/destroy test zgossip_msg_t *self = zgossip_msg_new (); assert (self); zgossip_msg_destroy (&self); // Create pair of sockets we can send through // We must bind before connect if we wish to remain compatible with ZeroMQ < v4 zsock_t *output = zsock_new (ZMQ_DEALER); assert (output); int rc = zsock_bind (output, "inproc://selftest-zgossip_msg"); assert (rc == 0); zsock_t *input = zsock_new (ZMQ_ROUTER); assert (input); rc = zsock_connect (input, "inproc://selftest-zgossip_msg"); assert (rc == 0); // Encode/send/decode and verify each message type int instance; self = zgossip_msg_new (); zgossip_msg_set_id (self, ZGOSSIP_MSG_HELLO); // Send twice zgossip_msg_send (self, output); zgossip_msg_send (self, output); for (instance = 0; instance < 2; instance++) { zgossip_msg_recv (self, input); assert (zgossip_msg_routing_id (self)); } zgossip_msg_set_id (self, ZGOSSIP_MSG_PUBLISH); zgossip_msg_set_key (self, "Life is short but Now lasts for ever"); zgossip_msg_set_value (self, "Life is short but Now lasts for ever"); zgossip_msg_set_ttl (self, 123); // Send twice zgossip_msg_send (self, output); zgossip_msg_send (self, output); for (instance = 0; instance < 2; instance++) { zgossip_msg_recv (self, input); assert (zgossip_msg_routing_id (self)); assert (streq (zgossip_msg_key (self), "Life is short but Now lasts for ever")); assert (streq (zgossip_msg_value (self), "Life is short but Now lasts for ever")); assert (zgossip_msg_ttl (self) == 123); } zgossip_msg_set_id (self, ZGOSSIP_MSG_PING); // Send twice zgossip_msg_send (self, output); zgossip_msg_send (self, output); for (instance = 0; instance < 2; instance++) { zgossip_msg_recv (self, input); assert (zgossip_msg_routing_id (self)); } zgossip_msg_set_id (self, ZGOSSIP_MSG_PONG); // Send twice zgossip_msg_send (self, output); zgossip_msg_send (self, output); for (instance = 0; instance < 2; instance++) { zgossip_msg_recv (self, input); assert (zgossip_msg_routing_id (self)); } zgossip_msg_set_id (self, ZGOSSIP_MSG_INVALID); // Send twice zgossip_msg_send (self, output); zgossip_msg_send (self, output); for (instance = 0; instance < 2; instance++) { zgossip_msg_recv (self, input); assert (zgossip_msg_routing_id (self)); } zgossip_msg_destroy (&self); zsock_destroy (&input); zsock_destroy (&output); // @end printf ("OK\n"); }