Masao Liu
Masao Liu

Reputation: 769

ZeroMQ always sends and gets 1 byte (using the cppzmq bindings)

The following server and client codes are slightly modified versions of the official tutorial. My interpretation of their outputs is that whatever they send or receive are always 1 byte. How can this happen?

server.cpp:

#include <zmq.hpp>
#include <string>
#include <iostream>
#ifndef _WIN32
#include <unistd.h>
#else
#include <windows.h>
#define sleep(n) Sleep(n)
#endif

int main () {
    zmq::context_t context(1);
    zmq::socket_t socket(context, ZMQ_REP);
    socket.bind("tcp://*:5555");
    while(true){
        zmq::message_t request;
        int length=socket.recv(&request);
        if(length >= 0)
            std::cout << length << " bytes received:" << std::string((char*)request.data(),length) << ":" << std::endl;
        sleep(1);
        zmq::message_t reply(5);
        memcpy(reply.data(), "World", 5);
        socket.send(reply);
    }
    return 0;
}

server ouput:

1 bytes received:H:

client.cpp:

#include <zmq.hpp>
#include <string>
#include <iostream>
int main ()
{
    zmq::context_t context(1);
    zmq::socket_t socket(context,ZMQ_REQ);
    socket.connect ("tcp://localhost:5555");
    zmq::message_t request(5);
    memcpy(request.data(),"Hello",5);
    std::cout << socket.send(request) << " bytes sent." << std::endl;
    zmq::message_t reply;
    int length=socket.recv(&reply);
    if(length >= 0)
        std::cout << length << " bytes received:" << std::string((char*)request.data(),length) << ":" << std::endl;
    return 0;
}

client output:

1 bytes sent.
1 bytes received:H:

Given such strange results, I start to suspect that I might misunderstand ZeroMQ's usage. I have read this " Minor Note on Strings" repeatedly:

ZeroMQ doesn't know anything about the data you send except its size in bytes. That means you are responsible for formatting it safely so that applications can read it back.

, which is followed by this sentence:

So let's establish the rule that ZeroMQ strings are length-specified and are sent on the wire without a trailing null.

These two paragraphs to me are contradictory. Exact what contents are sent by ZeroMQ?

Upvotes: 2

Views: 1658

Answers (3)

Masao Liu
Masao Liu

Reputation: 769

Thanks for all the helps! I think calling overloaded size_t send (const void *buf_, size_t len_, int flags_ = 0) instead of bool send (message_t &msg_, int flags_ = 0) makes the code a little more C++ than C. This is the working version that does not handle exceptions:

server.cpp:

//  Hello World server in C++
//  Binds REP socket to tcp://*:5555
//  Expects "Hello" from client, replies with "World"
//
#include <zmq.hpp>
#include <string>
#include <iostream>
#ifndef _WIN32
#include <unistd.h>
#else
#include <windows.h>
#define sleep(n) Sleep(n)
#endif

int main () {
    zmq::context_t context(1);
    zmq::socket_t socket(context, ZMQ_REP);
    socket.bind("tcp://*:5555");
    while(true){
        zmq::message_t request;
        if(socket.recv(&request))
            std::cout << request.size() << " bytes received:" << std::string((char*)request.data(),request.size()) << std::endl;
        sleep(1);
        std::cout << socket.send("world",5,0) << " bytes replied" << std::endl;
    }
    return 0;
}

client.cpp:

//  Hello World client in C++
//  Connects REQ socket to tcp://localhost:5555
//  Sends "Hello" to server, expects "World" back
//
#include <zmq.hpp>
#include <string>
#include <iostream>

int main ()
{
    zmq::context_t context(1);
    zmq::socket_t socket(context,ZMQ_REQ);
    socket.connect ("tcp://localhost:5555");
    std::cout << socket.send("Hello",5,0) << " bytes sent." << std::endl;
    zmq::message_t reply;
    if(socket.recv(&reply))
        std::cout << reply.size() << " bytes received:" << std::string((char*)reply.data(),reply.size()) << std::endl;
    return 0;
}

Upvotes: 3

Mateusz Piotrowski
Mateusz Piotrowski

Reputation: 9117

ZMQ can send and receive raw bytes. It is recommended to use zmq::message_t instead as it takes care of many edge cases for you and is generally nice to work with.

Look at the implementation of recv in zmq.hpp (link):

    inline bool recv (message_t *msg_, int flags_ = 0)
    {
        int nbytes = zmq_msg_recv (&(msg_->msg), ptr, flags_);
        if (nbytes >= 0)
            return true;
        if (zmq_errno () == EAGAIN)
            return false;
        throw error_t ();
    }

It returns true and this is why your length equals 1.

The paragraphs you quoted are not contradictory. It says that you should never assume that the data you received are NULL terminated which means that you shouldn't call printf("%s", request.data()) as printf will likely try to access the memory which isn't allocated. You could use printf("%.*s", (int)request.size(), request.data()) instead however.

After all, ZMQ sends the bytes you tell it to send so basically you can send anything and then receive the message and unpack it to make sense of it (for example when you send multiple sentences in one zmq::message_t and you use \0 as a delimiter between those sentences).

Upvotes: 1

Xaqq
Xaqq

Reputation: 4386

It seems you are using the functions that returns bool. So true converts to 1.

This explain why you believe you received and sent 1 bytes. I recommend you call size() on your message_t object instead.

Upvotes: 2

Related Questions