I am having trouble with a PUB/SUB in ZeroMQ.
After connecting everything, publisher publishes all messages ( socket's send message returns true ) but the SUB never receives them and blocks forever on .recv() function.
Here is the code I am using:
void startPublisher()
{
zmq::context_t zmq_context(1);
zmq::socket_t zmq_socket(zmq_context, ZMQ_PUB);
zmq_socket.bind("tcp://127.0.0.1:58951");
zmq::message_t msg(3);
memcpy(msg.data(), "abc", 3);
for(int i = 0; i < 10; i++)
zmq_socket.send(msg); // <-- always true
}
void startSubscriber()
{
zmq::context_t zmq_context(1);
zmq::socket_t zmq_socket(zmq_context, ZMQ_SUB);
zmq_socket.connect("tcp://127.0.0.1:58951");
zmq_socket.setsockopt(ZMQ_SUBSCRIBE, "", 0); // allow all messages
zmq::message_t msg(3);
zmq_socket.recv(&msg); // <-- blocks forever (message never received?)
}
Please note that I am runing these 2 functions in two different threads, starting SUB thread first, waiting for some time and then starting publisher thread ( also tried other way around with publisher sending messages in an endless-loop, but didn't work ).
What am I doing wrong here?
Based on your example, the following code works for me.
The problem is that the PUB / SUB pattern is a slow joiner, meaning you need to wait a while after binding the PUB socket and before sending any message.
#include <thread>
#include <zmq.hpp>
#include <iostream>
#include <unistd.h>
void startPublisher()
{
zmq::context_t zmq_context(1);
zmq::socket_t zmq_socket(zmq_context, ZMQ_PUB);
zmq_socket.bind("tcp://127.0.0.1:58951");
usleep(100000); // Sending message too fast after connexion will result in dropped message
zmq::message_t msg(3);
for(int i = 0; i < 10; i++) {
memcpy(msg.data(), "abc", 3);
zmq_socket.send(msg); // <-- always true
msg.rebuild(3);
usleep(1); // Temporisation between message; not necessary
}
}
volatile bool run = false;
void startSubscriber()
{
zmq::context_t zmq_context(1);
zmq::socket_t zmq_socket(zmq_context, ZMQ_SUB);
zmq_socket.connect("tcp://127.0.0.1:58951");
std::string TOPIC = "";
zmq_socket.setsockopt(ZMQ_SUBSCRIBE, TOPIC.c_str(), TOPIC.length()); // allow all messages
zmq_socket.setsockopt(ZMQ_RCVTIMEO, 1000); // Timeout to get out of the while loop
while(run) {
zmq::message_t msg;
int rc = zmq_socket.recv(&msg); // Works fine
if(rc) // Do no print trace when recv return from timeout
std::cout << std::string(static_cast<char*>(msg.data()), msg.size()) << std::endl;
}
}
int main() {
run = true;
std::thread t_sub(startSubscriber);
sleep(1); // Slow joiner in ZMQ PUB/SUB pattern
std::thread t_pub(startPublisher);
t_pub.join();
sleep(1);
run = false;
t_sub.join();
}
Related
I've been trying to send and receive structured messages using the push-pull pattern in ZeroMQ since two days, but no luck.
when I tried to print the received message content, it is printing garbage values. Kindly help me with the code.
here is the push code (server)
struct ab{
int a;
char arr[10];
};
void startPublisher()
{
// Prepare our context and publisher
zmq::context_t context (1);
zmq::socket_t publisher (context, zmq::socket_type::push);
publisher.bind("ipc://localsock");
while(1)
{
struct ab *messageB =new struct ab;
messageB->a=0;
strcpy(messageB->arr,"aaaaa");
const int length = sizeof(int)+ strlen("aaaaa") ;
zmq::message_t msg (length);
memcpy (msg.data (), &messageB, sizeof(messageB));
publisher.send(msg, zmq::send_flags::none);
}
}
int main ()
{
std::thread t_push(startPublisher);
t_push.join();
return 0;
}
and here is the pull code (client)
struct ab{
int a;
char arr[10];
};
void pull()
{
zmq::context_t context (1);
zmq::socket_t pull1 (context, zmq::socket_type::pull);
pull1.connect("ipc://localsock");
struct ab *messageB = (struct ab*)malloc(sizeof(struct ab));
const int length = sizeof(int)+ strlen("aaaaa");
while(1)
{
// zmq::message_t message(10);
zmq::message_t msg(length);
pull1.recv(msg);
memcpy ( messageB, msg.data (), sizeof(msg.data()));
std::cout << messageB->a << " " <<(char*)messageB->arr << std::endl;
}
}
int main () {
std::thread t1(pull);
t1.join();
return 0;
}
Issue
Hello! I am writing a bit of code to set up a Publisher/Subscriber Communication over tcp using ZeroMQ.
So far I have managed to send and receive the data according to topics. However, the subscriber reads the Topics as Messages and Messages as topics. i.e The Sub program prints:
Topic : Pub says Hello
Message : ABC
instead of
Topic : ABC
Message : Pub says Hello
I am at a bit of a loss here as I can't seem to figure out why this bug is happening. Any help is greatly appreciated! :)
Here is my code:
Publisher
int Send_Dummy_Data(const std::string tpc, std::string data)
{
zmq::context_t context(1);
zmq::socket_t output_publisher(context, ZMQ_PUB);
std::string transport("tcp://*:5556");
output_publisher.bind(transport);
while (true)
{
zmq::message_t topic(tpc.length());
zmq::message_t message(data.length());
memcpy(topic.data(), tpc.data(), tpc.size());
memcpy(message.data(), data.data(), data.length());
try
{
output_publisher.send(topic, zmq::send_flags::sndmore);
output_publisher.send(message, zmq::send_flags::dontwait);
}
catch (zmq::error_t &e)
{
std::cout << e.what() << std::endl;
return -1;
}
message.rebuild(data.length());
topic.rebuild(tpc.size());
}
return 0;
}
int main(int argc, char *argv[])
{
Send_Dummy_Data("ABC", "Pub says hello");
return 0;
}
Subscriber
void sub()
{
const std::string TOPIC = "ABC";
std::string transport("tcp://xxx.xxx.xxx.xx:5556");
zmq::context_t context_t(1); // -? What is context
zmq::socket_t subscriber(context_t, ZMQ_SUB);
subscriber.connect(transport);
subscriber.setsockopt(ZMQ_SUBSCRIBE, TOPIC.c_str(), TOPIC.length());
zmq::pollitem_t items[] = {
{zmq_socket, 0, ZMQ_POLLIN, 0},
};
while (true)
{
int rc = 0;
zmq::message_t rx_msg;
zmq::message_t topic;
zmq::poll(&items[0], 1, -1);
if (items[0].revents & ZMQ_POLLIN)
{
rc = subscriber.recv(&topic, ZMQ_RCVMORE);
rc = subscriber.recv(&rx_msg) && rc;
std::string rx_str;
std::string rx_topic;
rx_topic.assign(static_cast<char *>(topic.data()), topic.size());
rx_str.assign(static_cast<char *>(rx_msg.data()), rx_msg.size());
if (rc > 0)
{
std::cout << "Topic: " << rx_topic << std::endl;
std::cout << "Received: " << rx_str << std::endl;
}
}
}
}
int main()
{
std::thread t_pub(sub);
t_pub.join();
return 0;
}
This code looks odd, why are you passing the ZMQ_RCVMORE flag to recv?
rc = subscriber.recv(&topic, ZMQ_RCVMORE);
rc = subscriber.recv(&rx_msg) && rc;
You should use ZMQ_RCVMORE to check if there are more messages after the first recv like this (or similar)
int more;
size_t more_len = sizeof(more);
data_socket->getsockopt(ZMQ_RCVMORE, &more, &more_len);
What happens if you just change the code to this?
subscriber.recv(&topic);
subscriber.recv(&rx_msg);
You may be messing up the order flow by passing a bad flag to recv.
I am using the PUB/SUB model in ZeroMQ inside ROS.
The SUB-subscriber is allowed to stop just by pressing a Ctrl+C in the terminal.
However, every time, when I actually press the Ctrl+C, it stops by throwing the following error:
^Cterminate called after throwing an instance of 'zmq::error_t'
what(): Interrupted system call
Aborted (core dumped)
Below is the code snippet:
#include <ros/ros.h>
#include <zmq.hpp>
int main(int argc, char **argv)
{
ros::init(argc, argv, "node_name", ros::init_options::AnonymousName);
ros::NodeHandle nh;
ros::Publisher pub;
// Prepare context and publisher
zmq::context_t zmq_context(1);
zmq::socket_t zmq_socket(zmq_context, ZMQ_SUB);
zmq_socket.connect("tcp://192.168.1.20:9001");
std::string TOPIC = "";
zmq_socket.setsockopt(ZMQ_SUBSCRIBE, TOPIC.c_str(), TOPIC.length()); // allow all messages
int timeout = 1000; // Timeout to get out of the while loop since recv is blocking
zmq_socket.setsockopt(ZMQ_RCVTIMEO, &timeout, sizeof(timeout));
while (ros::ok())
{
zmq::message_t msg;
int rc = zmq_socket.recv(&msg);
if (rc)
{
//receive data and prepare it for publishing
pub.publish(data);
ros::spinOnce();
}
}
// Clean up the socket and context here
zmq_socket.close();
zmq_context.close();
return 0;
}
How to avoid the error so as to shut down the subscriber properly?
Without details about how the Ctrl+C was being trapped and handled, I would always add ( C++ binding details may differ version to version ):
int main(int argc, char **argv)
{
zmq_socket.connect( "tcp://192.168.1.20:9001" );
zmq_socket.setsockopt( ZMQ_LINGER, 0 ); // ALWAYS
...
while( ROS::ok() )
{
...
}
std::cout << "SIG:: will .close() after ROS::ok()-loop exit" << std::flush;
zmq_socket.close();
std::cout << "SIG:: will .term() after a Socket()-instance .close()'d" << std::flush;
zmq_context.close();
std::cout << "SIG:: will return 0 after a Context()-instance .term()'d" << std::flush;
return 0;
}
I realized that the issue is caused by int rc = zmq_socket.recv(&msg) hence I did following improvements-
I used try and catch block
Set ZMQ_LINGER as suggested by #user3666197
Below is the code snippet-
int linger = 0; // Proper shutdown ZeroMQ
zmq_socket.setsockopt(ZMQ_LINGER, &linger, sizeof(linger));
std::string socket_address = "tcp://192.168.1.20:9001";
zmq_socket.connect(socket_address.c_str());
ros::Duration duration(0.1); // in seconds (100 ms)
while (ros::ok())
{
zmq::message_t msg;
int rc = 0;
try
{
rc = zmq_socket.recv(&msg);
}
catch (zmq::error_t& e)
{
ROS_DEBUG_STREAM("ZMQ Error. " << e.what());
}
if (rc)
{
unsigned char* byte_ptr = static_cast<unsigned char*>(msg.data());
const int msg_length = msg.size();
// receive data and prepare it for publishing
pub.publish(data);
ros::spinOnce();
}
else
{
ROS_DEBUG_STREAM("Pointcloud recv() returned 0");
duration.sleep();
}
}
Thanks
I have zeromq-4.1.4 library and cppzmq installed on a real-time fast server and a slow client.
Both client and server have 2 ports for publishing and subscribing, communicating over TCP-IP.
The server sends messages at it's own fast rate. Client receives the latest message, does some slow computation and send the message back to server. Server reads the message if there is an incoming and processes it.
Problem is that old messages are not overwritten with new. Client always prints out older messages, and even if I switch off the server, messages continue to be queued from a receive buffer of the client.
Why does it happen? ZMQ_CONFLATE is set. Should not it just work?
As a workaround I though to put a client in a worker thread to work on a maximum rate and then keep the last message manually. But this is an overhead, as this is exactly what zeromq does when it send or receives messages as far as I understand.
Client/server code is same:
void ZeromqMessenger::init(const char* pubAddress, const char* subAddress, const char* syncAddress, int flags)
{
flags_ = flags;
int confl = 1;
// Prepare our context
context_ = new zmq::context_t(1);
// Prepare ZMQ publisher
publisher_ = new zmq::socket_t(*context_, ZMQ_PUB);
publisher_->bind(pubAddress);
publisher_->setsockopt(ZMQ_CONFLATE, &confl, sizeof(confl)); // Keep only last message
// Prepare ZMQ subscriber
subscriber_ = new zmq::socket_t(*this->context_, ZMQ_SUB);
subscriber_->connect(subAddress);
subscriber_->setsockopt(ZMQ_SUBSCRIBE, "", 0);
subscriber_->setsockopt(ZMQ_CONFLATE, &confl, sizeof(confl)); // Keep only last message
if (flags_ & ZMQ_SYNC_PUB)
{
syncService_ = new zmq::socket_t(*context_, ZMQ_REP);
syncService_->bind(syncAddress);
}
if (flags_ & ZMQ_SYNC_SUB)
{
// synchronize with publisher
syncService_ = new zmq::socket_t(*context_, ZMQ_REQ);
syncService_->connect(syncAddress);
// - send a synchronization request
zmq::message_t message(0);
syncService_->send(message);
// - wait for synchronization reply
zmq::message_t update;
syncService_->recv(&update);
}
}
void ZeromqMessenger::sync()
{
if (connected_)
return;
if (flags_ & ZMQ_SYNC_PUB)
{
//std::cout << "Waiting for subscribers" << std::endl;
if (subscribers_ < subscribers_expected_)
{
// - wait for synchronization request
zmq::message_t update;
if (syncService_->recv(&update, ZMQ_DONTWAIT))
{
// - send synchronization reply
zmq::message_t message(0);
syncService_->send(message);
subscribers_++;
}
}
if (subscribers_ == subscribers_expected_)
connected_ = true;
}
}
void ZeromqMessenger::send(const void* data, int size) const
{
zmq::message_t message(size);
memcpy(message.data(), data, size);
publisher_->send(message);
}
bool ZeromqMessenger::recv(void *data, int size, int flags) const
{
zmq::message_t update;
bool received = subscriber_->recv(&update, flags);
if(received)
memcpy(data, update.data(), size);
return received;
}
I implemented the threaded version and it works just fine. This is a very crude implementation with global variables, which shall be refined, but at least it works.
#include <zmq_messenger.h>
#include <iostream>
#include <thread>
#include <mutex>
std::string gSubAddress;
std::mutex gMtx;
const int gSize = 20*sizeof(double);
char gData[gSize];
void *worker_routine (void *context)
{
// Prepare ZMQ subscriber
int confl = 1;
zmq::socket_t* subscriber = new zmq::socket_t(*(zmq::context_t*)context, ZMQ_SUB);
subscriber->connect(gSubAddress.c_str());
subscriber->setsockopt(ZMQ_CONFLATE, &confl, sizeof(confl)); // Keep only last message
subscriber->setsockopt(ZMQ_SUBSCRIBE, "", 0);
while (1)
{
zmq::message_t update;
bool received = subscriber->recv(&update, ZMQ_DONTWAIT);
if(received)
{
gMtx.lock();
memcpy(gData, update.data(), gSize);
gMtx.unlock();
}
}
zmq_close(subscriber);
return NULL;
}
void ZeromqMessenger::init(const char* pubAddress, const char* subAddress, const char* syncAddress, int flags)
{
flags_ = flags;
int confl = 1;
// Prepare our context
context_ = new zmq::context_t(1);
// Prepare ZMQ publisher
publisher_ = new zmq::socket_t(*context_, ZMQ_PUB);
publisher_->bind(pubAddress);
publisher_->setsockopt(ZMQ_CONFLATE, &confl, sizeof(confl)); // Keep only last message
gSubAddress = std::string(subAddress);
pthread_create (&subscriber_worker_, NULL, worker_routine, context_);
if (flags_ & ZMQ_SYNC_PUB)
{
syncService_ = new zmq::socket_t(*context_, ZMQ_REP);
syncService_->bind(syncAddress);
}
if (flags_ & ZMQ_SYNC_SUB)
{
//std::cout << "Trying to connect" << std::endl;
// synchronize with publisher
syncService_ = new zmq::socket_t(*context_, ZMQ_REQ);
syncService_->connect(syncAddress);
// - send a synchronization request
zmq::message_t message(0);
syncService_->send(message);
// - wait for synchronization reply
zmq::message_t update;
syncService_->recv(&update);
// Third, get our updates and report how many we got
//std::cout << "Ready to receive" << std::endl;
}
}
void ZeromqMessenger::sync()
{
//std::cout << "sync" << std::endl;
if (connected_)
return;
if (flags_ & ZMQ_SYNC_PUB)
{
//std::cout << "Waiting for subscribers" << std::endl;
if (subscribers_ < subscribers_expected_)
{
// - wait for synchronization request
zmq::message_t update;
if (syncService_->recv(&update, ZMQ_DONTWAIT))
{
// - send synchronization reply
zmq::message_t message(0);
syncService_->send(message);
subscribers_++;
}
}
if (subscribers_ == subscribers_expected_)
connected_ = true;
//std::cout << subscribers_ << " subscriber(s) connected" << std::endl;
}
}
void ZeromqMessenger::send(const void* data, int size) const
{
zmq::message_t message(size);
memcpy(message.data(), data, size);
publisher_->send(message);
}
bool ZeromqMessenger::recv(void *data, int size, int flags) const
{
assert(gSize == size);
gMtx.lock();
memcpy(data, gData, size);
gMtx.unlock();
return true;
}
I am building a chat server. I have set identities for each socket. when i send a message to server(router). server parse the message and extract the UUID of intended message. Server then send message to client(corresponding to that identity).
I am doing this on client side:
#include "zhelpers.hpp"
#include <iostream>
#include <string>
#include "datamsg.pb.h"
zmq::context_t context(1);
zmq::socket_t backend (context, ZMQ_DEALER);
using namespace google::protobuf::io;
static void * worker_thread(void *arg) {
datamsg p = datamsg();
p.set_source("cl3");p.set_port(5559);
std::string input;
while(1){
getline (std::cin, input); //separate thread for asynchronous input
p.set_messagee(input.substr(3,(input.length()-1)));
p.set_destination(input.substr(0,3));
std::string str;
p.SerializeToString(&str);
zmq::message_t request (str.size());
memcpy ((void *) request.data (), str.c_str(),str.size());
backend.send(request);
}
}
int main(void) {
backend.setsockopt( ZMQ_IDENTITY, "cl3", 3);
backend.connect("tcp://localhost:5559");
zmq::message_t received_instant_msg;
datamsg receive_parsed;
pthread_t worker;
pthread_create(&worker, NULL, worker_thread, NULL);
std::cout <<"you are joinning" << std::endl;
while(1){
zmq::pollitem_t items [] = {
{ backend, 0, ZMQ_POLLIN, 0 }
};
zmq::poll (items, 1, -1);
if (items [0].revents & ZMQ_POLLIN) {
std::string emptystring = s_recv (backend); //first string is empty ""
backend.recv(&received_instant_msg); //Now parsing the message down there
receive_parsed.ParseFromArray(received_instant_msg.data(), received_instant_msg.size());
// printing the parsed messages here
} //ending if
} //while
return 0;
}
I cannot get past cin input to receive message. Once i enter input at console and send it , then i can receive queued messages from server.:(
How would I deal this.? Can multithreading approach be adopted this way? if yes then how?