I am trying to familiarize myself with ZeroMQ by creating a simple socket communication betwenn a publisher and a subscriber to send a test message. However, I can't find the information I want on how to put a string inside a zmq::message_t type message. Indications pointed to the use of "std::memcpy(message.data(), ms.data(), ms.size())" which I tried. However, by debuging the coding using Watchs, I see that the message is still empty after execution:
WATCH IMAGE and it is also empty when I print it out using a cout: Cmd
Is there another way to assign a string to a zmq::message_t message or is there something else wrong here?
My entire code is:
int main()
{
zmq::context_t context(1);
zmq::socket_t pub(context, ZMQ_PUB);
pub.bind("tcp://*:5555");
std::cout << "Pub Connected" << std::endl;
zmq::socket_t sub(context, ZMQ_SUB);
sub.connect("tcp://localhost:5555");
std::cout << "Sub Connected" << std::endl;
std::stringstream s;
s << "Hello World";
auto ms = s.str();
zmq::message_t message(ms.size());
memcpy(message.data(), ms.c_str(), ms.length());
pub.send(message, zmq::send_flags::none);
std::cout << "message: " << message << std::endl;
zmq_sleep(1);
sub.set(zmq::sockopt::subscribe, "Hello World");
zmq::message_t rx_msg;
sub.recv(rx_msg,zmq::recv_flags::none);
std::string rx_str;
rx_str.assign(static_cast<char*>(rx_msg.data()), rx_msg.size());
std::cout << "Message: " << rx_str << "received!" << std::endl;
}
There is a constructor for zmq::message_t that has the signature (docs)
message_t(const void *data_, size_t size)
so you could use this like
zmq::message_t message(static_cast<void*>(ms.data()), ms.size());
Related
I have tried following the tutorial from boost, however the API is not identical so I have had to guess some parts.
My attempt so far is shown bellow:
#include <iostream>
#include <experimental/internet>
#include <experimental/socket>
#include <thread>
#include <chrono>
using namespace std::experimental;
int main(int argc, char* argv[])
{
std::thread server = std::thread([]()
{
std::cout << "Starting server" << std::endl;
net::io_context context;
net::ip::tcp::endpoint endpoint{net::ip::tcp::v4(), 1234};
net::ip::tcp::acceptor acceptor{context, endpoint};
acceptor.non_blocking(true);
std::cout << "opened server on " << endpoint << std::endl;
std::error_code error;
net::ip::tcp::socket socket(context);
while (true)
{
socket = acceptor.accept(error); //accept connections
if (!error) //if connected with a client
{
std::cout << "Connected to client!" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2));
std::string data = "Hello World!";
net::const_buffer buf(&data, sizeof(data));
socket.send(buf);
std::cout << "Sent data!" << std::endl;
while(true) {}
}
}
});
std::thread client = std::thread([]()
{
net::io_context context;
net::ip::tcp::socket socket(context);
net::ip::tcp::endpoint server{net::ip::tcp::v4(), 1234};
std::error_code error;
while(true)
{
socket.connect(server, error); //attempt to connect
if (!error) //if connected
{
std::cout << "Connected to server!" << std::endl;
net::mutable_buffer buf;
while(buf.size() == 0)
{
socket.receive(buf);
}
std::cout << "Received data!" << std::endl;
std::cout << buf.data() << std::endl;
while(true) {}
}
}
});
server.join();
return 0;
}
The sever and client connect, but the message is not received by the client. The output from the program above is:
Starting server
opened server on 0.0.0.0:1234
Connected to server!
Connected to client!
Sent data!
And then it waits forever.
How do I get the socket to correctly receive the data?
This
std::string data = "Hello World!";
net::const_buffer buf(&data, sizeof(data));
is wrong. You want to send content of data string, not its internal bytes. &data gives you a pointer to underlying data of string instance, not its content. If you want to create buffer which represents content of data you can do:
const std::string data = "Hello World!";
net::const_buffer buf = net::buffer(data);
This
net::mutable_buffer buf;
while(buf.size() == 0)
{
socket.receive(buf);
}
gives you infinite loop because initial size of buf is 0, so receive reads 0 bytes and returns. Then while condition is checked, buf's size is still 0, and the loop goes on.
Before calling receive you need to specify the size of buffer - it indicates how many bytes must be read. You are sending Hello World! so
std::string msg;
msg.resize(12); // prepare space for incoming data
net::mutable_buffer buf = net::buffer(msg);
socket.receive(buf);
std::cout << "I got: " << msg << std::endl;
I try to run simple ZMQ application ( ROUTER/DEALER ).
I just send a request from DEALER to ROUTER, send it back. But DEALER cannot receive it.
I run it in one process ( ROUTER has its own thread ).
#include <zmq.hpp>
#include <string>
#include <iostream>
#include <thread>
void router()
{
zmq::context_t context(1);
zmq::socket_t socket(context, ZMQ_ROUTER);
socket.bind("tcp://*:5561");
while(1)
{
// Wait for next request from client
zmq::message_t reply;
socket.recv (&reply);
std::cout << "Router: Received request" << std::endl;
// Send reply back to client
std::string string= "example";
zmq::message_t message(string.size());
memcpy (message.data(), string.data(), string.size());
std::cout << "Router: Sending" << std::endl;
socket.send (message);
}
}
int main ()
{
std::thread t{&router};
// Prepare our context and socket
zmq::context_t context (2);
zmq::socket_t socket (context, ZMQ_DEALER);
std::cout << "Dealer: Connecting to hello world server…" << std::endl;
socket.connect ("tcp://127.0.0.1:5561");
for (int i = 0; i != 10; i++)
{
zmq::message_t request (5);
memcpy (request.data (), "Hello", 5);
std::cout << "Dealer: Sending Hello " << i << "…" << std::endl;
socket.send (request);
zmq::message_t reply;
socket.recv (&reply);
std::cout << "Dealer: Received " << i << std::endl;
}
return 0;
}
I have an output:
Dealer: Connecting to hello world server…
Dealer: Sending Hello 0…
Router: Received request
Router: Sending
Router: Received request
Router: Sending
From ZMQ's documentation on socket :
When receiving messages a ZMQ_ROUTER socket shall prepend a message
part containing the identity of the originating peer to the message
before passing it to the application. Messages received are
fair-queued from among all connected peers. When sending messages a
ZMQ_ROUTER socket shall remove the first part of the message and use
it to determine the identity of the peer the message shall be routed
to.
So modify your code to something like this :
#include <zmq.hpp>
#include <string>
#include <iostream>
#include <thread>
#include <unistd.h>
void router()
{
zmq::context_t context(1);
zmq::socket_t socket(context, ZMQ_ROUTER);
socket.bind("tcp://*:5561");
while(1) // Fix that infinite loop or your thread won't join
{
// Wait for next request from client
zmq::message_t id;
socket.recv (&id);
zmq::message_t reply;
socket.recv (&reply);
std::cout << std::string(static_cast<char*>(reply.data()),reply.size()) << std::endl;
std::cout << "Router: Received request" << std::endl;
// Send reply back to client
zmq::message_t copy_id;
copy_id.copy(&id);
std::string string= "example";
zmq::message_t message(string.size());
memcpy (message.data(), string.data(), string.size());
std::cout << "Router: Sending" << std::endl;
socket.send(id, ZMQ_SNDMORE);
socket.send(message);
}
sleep(1);
socket.setsockopt(ZMQ_LINGER, 0);
socket.close();
context.close();
}
int main ()
{
std::thread t{&router};
// Prepare our context and socket
zmq::context_t context (2);
zmq::socket_t socket (context, ZMQ_DEALER);
std::cout << "Dealer: Connecting to hello world server…" << std::endl;
socket.connect ("tcp://127.0.0.1:5561");
for (int i = 0; i != 10; i++)
{
zmq::message_t request (5);
memcpy (request.data (), "Hello", 5);
std::cout << "Dealer: Sending Hello " << i << "…" << std::endl;
socket.send(request);
zmq::message_t reply;
socket.recv(&reply);
std::cout << "Dealer: Received " << i << std::endl;
}
socket.setsockopt(ZMQ_LINGER, 0);
socket.close();
context.close();
t.join();
return 0;
}
I am trying to send a reasonably big Flatbuffers object over the network via ZMQ and then read it using C++. When accessing the object, I get unhandled exceptions that I don't know how to solve. Even this minimal example fails:
The flatbuffers schema:
namespace flatbuffer;
table TestBuf {
testStatus:bool;
testNumber:double;
testInt:int;
}
root_type TestBuf;
The main.cpp using the REP socket:
int main() {
zmq::context_t context(1);
zmq::socket_t socket(context, ZMQ_REP);
socket.bind("tcp://*:5555");
std::cout << "Listening for requests." << std::endl;
std::cout << "-----" << std::endl;
double count = 0;
while (1) {
zmq::message_t request;
socket.recv(&request);
// Read incoming data
auto reqmsg = flatbuffer::GetTestBuf(&request);
std::cout << "Received: " << reqmsg << std::endl;
flatbuffers::FlatBufferBuilder fbb;
flatbuffer::TestBufBuilder builder(fbb);
count++;
builder.add_testNumber(count);
std::cout << "Sending " << count << std::endl;
auto response = builder.Finish();
fbb.Finish(response);
// Send the flatbuffer
int buffersize = fbb.GetSize();
zmq::message_t message(buffersize);
memcpy((void *)message.data(), fbb.GetBufferPointer(), buffersize);
socket.send(message);
}
return 0;
}
The main.cpp using the REQ socket:
int main() {
// Prepare ZMQ context and socket
zmq::context_t context(1);
zmq::socket_t socket(context, ZMQ_REQ);
std::cout << "Sending out data requests." << std::endl;
socket.connect("tcp://localhost:5555");
double count = 0;
while (1) {
// Formulate response
flatbuffers::FlatBufferBuilder fbb;
flatbuffer::TestBufBuilder builder(fbb);
count++;
builder.add_testNumber(count);
auto response = builder.Finish();
fbb.Finish(response);
// Send the flatbuffer
std::cout << "Sending. " << count << ". ";
int buffersize = fbb.GetSize();
zmq::message_t message(buffersize);
memcpy((void *)message.data(), fbb.GetBufferPointer(), buffersize);
socket.send(message);
std::cout << "Sent. ";
// Receive reply
zmq::message_t reply;
socket.recv(&reply);
// Read the data
auto inmsg = flatbuffer::GetTestBuf(&reply);
std::cout << " Received reply: " << inmsg << std::endl;
//auto num = inmsg->testNumber();
//std::cout << num << " test number.";
}
return 0;
}
This code runs fine and displays (I think) the raw buffer each program is receiving. Strangely, it is not changing, although the content of the message should be. If I uncomment the last two lines and try to access inmsg->testNumber(), I get this error message:
Unhandled exception at 0x000000013F373C53 in KUKAREQ.exe: 0xC0000005: Access violation reading location 0x00000000004B35D8.
I have sent Flatbuffers objects through ZMQ successfully before, but I have not read them in C++. I am fairly sure I followed the Flatbuffers tutorial closely, but something is obviously going wrong. Pointers? Buffer sizes? Either way I would appreciate help.
Edit: To clarify my comment on the accepted answer, the offending line was:
auto inmsg = flatbuffer::GetTestBuf(&reply);
It has to be changed to:
auto inmsg = flatbuffer::GetTestBuf(reply.data());
Whoever reads this question may also be interested to know that I later came across a bug which occurs when the FlatBufferBuilder functions are not called in the correct order. Apparently the order in which the Flatbuffers object is built is important. Finding that one took me a while - novices watch out.
Not familiar with ZeroMQ, but flatbuffer::GetTestBuf(&request) this looks problematic.. you need to pass the buffer, not the message structure. Likely request.data() or similar works better.
In general, if it crashes in FlatBuffers, you should use the verifier to verify the buffer you're passing to FlatBuffers. If that fails, it means you're not passing legal data to FlatBuffers, as is the case here.
Also, you may want to check if ZeroMQ can send buffers without copying, will be faster.
I am a new-by to ZeroMQ and make my way through the C++ hello-world example of the echo client-server pattern (Request-Reply). The server looks like:
//
// 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>
#include <unistd.h>
int main () {
// Prepare our context and socket
zmq::context_t context (1);
zmq::socket_t socket (context, ZMQ_REP);
socket.bind ("tcp://*:5555");
while (true) {
zmq::message_t request;
// Wait for next request from client
socket.recv (&request);
std::cout << "Received Hello" << std::endl;
// Do some 'work'
sleep (1);
// Send reply back to client
zmq::message_t reply (5);
memcpy ((void *) reply.data (), "World", 5);
socket.send (reply);
}
return 0;
}
Now my question: How can I access / read the real data that socket.recv() ? Trying:
std::cout << request << std::endl;
resulted in an error message:
error: no match for ‘operator<<’ in ‘std::operator<< [with _Traits =
std::char_traits<char>](((std::basic_ostream<char, std::char_traits<char> >&)
(& std::cout)), ((const char*)"Received Hello")) << request’
The same goes for the client side that is sending the message. I don't find a way to display the real message...
The hello world example goes only half way and outputs the hard-coded values:
std::cout << "Received Hello" << std::endl;
Printing the actual response can be done as follows:
zmq::message_t reply;
socket.recv (&reply);
std::string rpl = std::string(static_cast<char*>(reply.data()), reply.size());
std::cout << rpl << std::endl;
There are some other useful examples in zhelpers.hpp.
I found that the following does what I want:
zmq::message_t request (msglen);
memcpy ( (void *) request.data(), myMessage, msglen);
char * requestmsg = new char [msglen];
memcpy (requestmsg, request.data(), request.size());
requestsocket.send (request);
std::cout << "Sending " << requestmsg << std::endl;
where msglen is of type int and myMessage is const char * tyoe.
In this way, the server receives a human readable message.
Hope this is not against any zeromq rules...
While I think we need to go through the guide before we can write elegant ZeroMQ codes. I found lines of simple codes from the HELLO WORLD example for extracting data received from the socket and sending back response:
zmq::message_t request;
socket.recv (&request);
std::cout << "Received request: [" << (char*) request.data() << "]" << std::endl;
// Do some 'work'
Sleep (1);
// Send reply back to client
zmq::message_t reply (6);
memcpy ((void *) reply.data (), "World", 6);
socket.send (reply);
However, this solution does not specify the length of received data, following Nikolai Koudelia's way above, I make a string for received data:
std::cout << "Received request: [" << std::string(static_cast<char*>(request.data()), request.size()) << "]" << std::endl;
I can't get ZeroMQ C++ wrapper to receive multipart messages. The same code using C version works just fine, but it leads to an exception with no explanations at all with C++. The multipart handling code is as follows:
int _tmain(int argc, _TCHAR* argv[])
{
zmq::context_t context(1);
zmq::socket_t socket(context, ZMQ_REP);
socket.bind("tcp://*:5555");
while(true) {
// the following two lines lead to exception
zmq::message_t request;
socket.recv(&request);
//zmq_msg_t message;
//zmq_msg_init (&message);
//zmq_recv (socket, &message, 0);
}
return 0;
}
It is extremely simple; this version does not work. but if I comment out the first two lines in the while loop and uncomment the currently commented (C version) code, it works.
This is Windows XP sp3, Zeromq 2.1.1 and Visual Studio 2010 Express.
If I send single part messages, both versions work fine.
What am I doing wrong?
I'm also a newbie in ZMQ and I too had to struggle a lot in order to understand multipart messaging using REP/REQ in ZeroMQ. I had to go through multiple resources and stitch data in order to understand this. I think this answer will help many seekers in the near future that's why I am sharing the client and server code here. I have tested this code and it is working perfectly fine. However, being a newbie there are chances that I would have missed something vital. Please share your valuable inputs.
Server Code
void
serverMultipartREPREQ()
{
try
{
zmq::context_t context(1);
zmq::socket_t socket(context, ZMQ_REP);
socket.bind("tcp://*:5556");
std::cout << "Listening at port 5556..." << std::endl;
zmq::message_t reply;
socket.recv(reply, zmq::recv_flags::none);
auto rep = std::string(static_cast<char*> (reply.data()), reply.size());
std::cout << "Received: " << rep << std::endl;
while(1)
{
if (input == "exit")
break;
for (int j = 0; j < 3; ++j)
{
std::string s("Message no - " + std::to_string(j));
zmq::message_t message(s.length());
memcpy(message.data(), s.c_str(), s.length());
std::cout << "Sending: " << s << std::endl;
if (j != 2)
socket.send(message, zmq::send_flags::sndmore);
else
socket.send(message, zmq::send_flags::none);
}
}
}
catch (const zmq::error_t& ze)
{
std::cout << "Exception: " << ze.what() << std::endl;
}
Sleep(5000);
}
Client code
void
clientMultipartREQREP()
{
try
{
zmq::context_t context(1);
std::cout << "Connecting to socket at 5556" << std::endl;
zmq::socket_t socket(context, ZMQ_REQ);
socket.connect("tcp://localhost:5556");
std::cout << "Connected to socket at 5556" << std::endl;
std::string msg("Hii this is client...");
zmq::message_t message(msg.length());
memcpy(message.data(), msg.c_str(), msg.length());
socket.send(message, zmq::send_flags::none); // send to server (request message)
while (true)
{
__int64 more = 1;
if (more)
{
zmq::message_t message;
socket.recv(message, zmq::recv_flags::none);
auto rep = std::string(static_cast<char*> (message.data()), message.size());
std::cout << "Reading from client: " << rep << std::endl;
size_t size = sizeof(__int64);
socket.getsockopt(ZMQ_RCVMORE, &more, &size); // if msg is not the last one then more = 1 else more = 0
}
else
{
std::cout << "Done..." << std::endl;
break;
}
}
}
catch (const zmq::error_t& ze)
{
std::cout << "Exception: " << ze.what() << std::endl;
}
Sleep(5000);
}
Probably C version of code doesn't work either, but you don't check the return code of zmq_recv, so you don't notice it. Also, when receiving miltipart messages you should check if there are more message parts to be received through the socket, like this:
int64_t more = 0;
size_t more_size = sizeof(more);
socket.getsockopt(ZMQ_RCVMORE, &more, &more_size);
if (more != 0)
{
//has more parts
}
Also, take a look at ZmqMessage C++ library designed specifically for Sending and receiving ZeroMQ multipart messages.
I decided to use the C version of the code. In general all examples seem to be in C anyway.