Zeromq C++ Subscriber not receiving message - c++

Publisher code
int main()
{
zmq::context_t ctx(0);
zmq::socket_t publisher(ctx, zmq::socket_type::pub);
publisher.bind("inproc://#1");
// Give the subscribers a chance to connect, so they don't lose any messages
std::this_thread::sleep_for(std::chrono::seconds(5));
std::cout << "Publisher Started Publishing messages";
while (true)
{
// Write three messages, each with an envelope and content
publisher.send(zmq::str_buffer("A"), zmq::send_flags::sndmore);
publisher.send(zmq::str_buffer("Message in A envelope"));
publisher.send(zmq::str_buffer("B"), zmq::send_flags::sndmore);
publisher.send(zmq::str_buffer("Message in B envelope"));
publisher.send(zmq::str_buffer("C"), zmq::send_flags::sndmore);
publisher.send(zmq::str_buffer("Message in C envelope"));
std::this_thread::sleep_for(std::chrono::seconds(10));
}
}
Subscriber code
int main() {
zmq::context_t ctx(0);
zmq::socket_t subscriber(ctx, zmq::socket_type::sub);
subscriber.connect("inproc://#1");
// Thread3 opens ALL envelopes
subscriber.set(zmq::sockopt::subscribe, "A");
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
while (1) {
// Receive all parts of the message
std::vector<zmq::message_t> recv_msgs; std::cout << "Connected to Publisher";
zmq::recv_result_t result =
zmq::recv_multipart(subscriber, std::back_inserter(recv_msgs));
assert(result && "recv failed");
assert(*result == 2);
std::cout << "Thread3: [" << recv_msgs[0].to_string() << "] "
<< recv_msgs[1].to_string() << std::endl;
}
}
I trying to apply zeromq pub-sub pattern created both publisher and subscriber in c++. The message published by publisher is not getting received by the subscriber.

Related

Send request from a server to a connected client with RSocket C++

could someone kindly help me with the RSocket issue? I'm trying to send a request from the Server to a connected Client, but the Client Responder does not fire callbacks. I'm using the RSocket-cpp library.
Server code:
TcpConnectionAcceptor::Options opts;
opts.address = folly::SocketAddress("::", FLAGS_port);
opts.threads = 2;
// RSocket server accepting on TCP
auto rs = std::make_unique<TcpConnectionAcceptor>(std::move(opts));
auto serverThread = std::thread([&rs] {
// start accepting connections
rs->start(
[](std::unique_ptr<DuplexConnection> connection, folly::EventBase& eventBase) {
LOG(INFO) << "new incoming connected" << std::endl;
auto client = RSocket::createClientFromConnection(
std::move(connection), *eventBase.getEventBase(), SetupParameters(), nullptr, std::make_shared<GenericRequestResponseResponder>());
LOG(INFO) << "send data" << std::endl;
client->getRequester()->requestResponse(Payload("hello2"))->subscribe([](Payload p) {
LOG(INFO) << "Received1 >> " << p.moveDataToString() << std::endl;
});
LOG(INFO) << "request is sent from server" << std::endl;
});
});
Output:
I0327 07:11:33.583813 23622 RequestResponseHelloWorld_Server.cpp:95] new incoming connected
I0327 07:11:33.602982 23622 RequestResponseHelloWorld_Server.cpp:100] send data
I0327 07:11:33.604566 23622 RequestResponseHelloWorld_Server.cpp:105] request is sent from server
Client code:
class GenericRequestResponseResponder : public rsocket::RSocketResponder
{
public:
std::shared_ptr<Single<Payload>> handleRequestResponse(
Payload request,
StreamId /*streamId*/) override
{
LOG(INFO) << "GenericRequestResponseResponder.handleRequestResponse "
<< request << std::endl;
// string from payload data
auto requestString = request.moveDataToString();
return Single<Payload>::create(
[name = std::move(requestString)](auto subscriber) {
std::stringstream ss;
ss << "Ack " << name << "!";
std::string s = ss.str();
subscriber->onSubscribe(SingleSubscriptions::empty());
subscriber->onSuccess(Payload(s, "metadata"));
});
}
void handleFireAndForget(
rsocket::Payload request,
rsocket::StreamId /*streamId*/) override
{
LOG(INFO) << "GenericRequestResponseResponder.handleRequestResponse "
<< request << std::endl;
}
};
folly::SocketAddress address{folly::SocketAddress(host, port)};
std::shared_ptr<TelemetryConnection> connection{nullptr};
RSocket::createConnectedClient(
std::make_unique<TcpConnectionFactory>(
*m_worker->getEventBase(), std::move(address)), SetupParameters(), std::make_shared<GenericRequestResponseResponder>())
.thenValue([this, host, port, &connection](auto&& client) {
LOG(INFO) << "client is created" << std::endl;
m_clientList.append(client);
})
.thenError(
folly::tag_t<std::exception>{},
[&](const std::exception&) {
LOG(ERROR) << "connection failed";
}).get();
I was expecting GenericRequestResponseResponder::``handleRequestResponse fired when the server sends the request, but client output is empty

How to Send messages between server and client using C++ standard library networking TS

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;

ZMQ Hello world does not work

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;
}

Ros subscriber is processing an old message

I am currently trying to pass some information between two ros node.
The publisher, being a ros node written in python publishes a string on to a topic, and the subscriber, being a ros node written in cpp, has a callback function which reacts on messages being sent to that topic.
My problem is with the callback function in the cpp part, which doesn't process the recently published message, but only the one sent just before the new one.The cpp subscriber is basically one message behind.
Echoing the topic shows that the message being published on to the topic is the correct one, so something is must be wrong on the cpp part.
The callback is currently only printing the incomming message, and nothing else.
The subscriber on the CPP part is initialized as such:
status(nodehandle.subscribe("/status",10000,&wrapper::callback, this))
void wrapper::callback(const std_msgs::String& command)
{
ROS_ERROR_STREAM("RECEIVED a callback");
std::cout <<"Received unconverted message: "<< command.data << std::endl;
}
I tried changing the queue_size with no effect.
I am sure that the python code is publishing the right message, by debugging it via echoing the topic.
And sending multiple messages, proves that the messages received on the cpp part is delayed by one message.
Why am I not able to read the newest message published on to the topic?
MWE:
ros::NodeHandle nodehandle;
ros::Publisher control_cpp_to_python;
ros::Publisher control_front_to_python;
ros::Subscriber status_manual;
ros::Subscriber status_auto;
ros::Rate loop_rate;
void callback_manual(const std_msgs::String& command)
{
ROS_ERROR_STREAM("RECEIVED a callback");
std::cout <<"Received unconverted message - manuak: "<< command.data << std::endl;
if(checked == true)
{
std::cout << "Message received from correct callback - Manual" << std::endl;
checked = false;
thread_running=false;
}
else
{
checked = true;
}
}
void callback_auto(const std_msgs::String& command)
{
ROS_ERROR_STREAM("RECEIVED a callback");
std::cout <<"Received unconverted message - auto: "<< command.data << std::endl;
if(checked == true)
{
std::cout << "Message received from correct callback - Manual" << std::endl;
checked = false;
thread_running=false;
}
else
{
checked = true;
}
}
while(thread_running)
{
loop_rate.sleep();
if(!thread_running) break;
if(mode == "manual")
{
std_msgs::Int8 msg;
std::bitset<4> msg_bit;
msg_bit.set(3,1);
msg_bit.set(2,1);
msg_bit.set(1,1);
msg_bit.set(0,0);
std::cout << "Sending message in manual mode!" << std::endl;
std::cout << "Message being: "<< msg_bit << std::endl;
msg.data = int(msg_bit.to_ulong());
control_front_to_python.publish(msg);
}
if(mode == "auto")
{
std::cout << "Publishing message in auto" << std::endl;
std_msgs::Int8 msg;
msg.data = 8;
control_cpp_to_python.publish(msg);
}
ros::spinOnce();
}
I guess this is as scraped as it can be. I guess the issue must be due to me setting the thread_running to false inside the callback. I've currently fixed it by the checked bool as a temporary fix.

How can I receive multipart messages with ZeroMQ?

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.