ZeroMQ Pub/Sub - Message and Topic being read in wrong order - c++

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.

Related

Sending and receiving structure message in ZeroMQ

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

socket recv read no message

The following code is an application supposed to be communicating between two applications. In one exe (A) user type a message and the message is printed in the other exe(B).
The flow of the program:
Both exe calls connectTo so they are ready to send and receive messages between.
User type a message to be send in A console window, which calls sendMsg in A exe. When message is received in B, the message is printed in B console window.
The problem is that the message received is sometimes empty. When I enter 1234 ten times in A, ten messages are printed in B with only 5-6 of them are 1234 and the rest are empty. The situation is the same from B to A.
++++++++++++++++++++++++++
whole program
++++++++++++++++++++++++++
Header.h
#pragma once
#include <WinSock2.h>
#include <Windows.h>
#include <mutex>
#include <thread>
class CommuWin
{
private:
std::mutex m_accessMutexSend;
std::mutex m_accessMutexReceive;
std::thread m_sendThread;
std::thread m_receiveThread;
bool m_IsSendReady = false;
bool m_IsRecvReady = false;
SOCKET m_outSocket;
SOCKADDR_IN m_outAddr;
SOCKET m_inSocket;
SOCKADDR_IN m_inAddr;
public:
CommuWin(int InPort, int OutPort);
~CommuWin();
int connectTo();
int sendMsg(const char* message);
int StartReceiveMsg();
bool GetRecvStatus();
bool GetSendStatus();
private:
void SetRecvStatus(bool ready);
void SetSendStatus(bool ready);
int SetupRecvEnd();
int SetupSendEnd();
int sendMsgTo(const char* message);
int ReceiveMsgFrom();
};
Source.cpp
#pragma comment(lib, "Ws2_32.lib")
#include "Header.h"
#define OKAY (1)
#define ERROR (-1)
#define MAX_MSG_SIZE (200)
class NetworkServices
{
public:
static int sendMessage(SOCKET curSocket, const char* message, int messageSize);
static int receiveMessage(SOCKET curSocket, char * buffer, int bufSize);
};
int NetworkServices::sendMessage(SOCKET curSocket, const char* message, int messageSize)
{
return send(curSocket, message, messageSize, 0);
}
int NetworkServices::receiveMessage(SOCKET curSocket, char * buffer, int bufSize)
{
return recv(curSocket, buffer, bufSize, 0);
}
CommuWin::CommuWin(int InPort, int OutPort)
{
WSAData wsaData;
WORD DLLVersion;
DLLVersion = MAKEWORD(2, 1);
int r = WSAStartup(DLLVersion, &wsaData);
///////////////////////////////////////////////////////////////////////
m_outSocket = socket(AF_INET, SOCK_STREAM, NULL);
m_outAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
m_outAddr.sin_family = AF_INET;
m_outAddr.sin_port = htons(OutPort);
m_inSocket = socket(AF_INET, SOCK_STREAM, NULL);
m_inAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
m_inAddr.sin_family = AF_INET;
m_inAddr.sin_port = htons(InPort);
}
CommuWin::~CommuWin()
{
}
int CommuWin::connectTo()
{
printf("connect to");
printf("\n");
m_sendThread = std::thread(
&CommuWin::SetupSendEnd,
this);
m_receiveThread = std::thread(
&CommuWin::SetupRecvEnd,
this);
return OKAY;
}
int CommuWin::SetupSendEnd()
{
SOCKET sListen;
sListen = socket(AF_INET, SOCK_STREAM, NULL);
bind(sListen, (SOCKADDR*)&m_outAddr, sizeof(m_outAddr));
listen(sListen, SOMAXCONN);
m_outSocket = accept(sListen, NULL, NULL);
if (m_outSocket != INVALID_SOCKET)
{
SetSendStatus(true);
printf("accepted\n");
}
return OKAY;
}
int CommuWin::SetupRecvEnd()
{
int connectSucceed = 0;
do
{
Sleep(1000);
connectSucceed = connect(m_inSocket, (SOCKADDR*)&m_inAddr, sizeof(m_inAddr));
} while (connectSucceed == SOCKET_ERROR);
SetRecvStatus(true);
printf("connected\n");
return OKAY;
}
int CommuWin::sendMsg(const char* message)
{
if (GetSendStatus())
{
m_sendThread.detach();
m_sendThread = std::thread(
&CommuWin::sendMsgTo,
this,
message);
}
return OKAY;
}
int CommuWin::sendMsgTo(const char* message)
{
NetworkServices::sendMessage(m_outSocket, message, (int)strlen(message));
return OKAY;
}
int CommuWin::StartReceiveMsg()
{
if (GetRecvStatus())
{
m_receiveThread.detach();
m_receiveThread = std::thread(
&CommuWin::ReceiveMsgFrom,
this);
}
return OKAY;
}
int CommuWin::ReceiveMsgFrom()
{
while (true)
{
char message[MAX_MSG_SIZE];
ZeroMemory(message, MAX_MSG_SIZE);
NetworkServices::receiveMessage(m_inSocket, message, sizeof(message));
printf(message);
printf("\n");
}
return OKAY;
}
void CommuWin::SetRecvStatus(bool ready)
{
std::lock_guard<std::mutex> lock(m_accessMutexReceive);
m_IsRecvReady = ready;
}
void CommuWin::SetSendStatus(bool ready)
{
std::lock_guard<std::mutex> lock(m_accessMutexSend);
m_IsSendReady = ready;
}
bool CommuWin::GetRecvStatus()
{
std::lock_guard<std::mutex> lock(m_accessMutexReceive);
return m_IsRecvReady;
}
bool CommuWin::GetSendStatus()
{
std::lock_guard<std::mutex> lock(m_accessMutexSend);
return m_IsSendReady;
}
main.cpp
#include "stdafx.h"
#include "Header.h"
#include <iostream>
#include <string>
int main(int argc, char *argv[])
{
std::cout << argc <<std::endl;
int Inport = std::stoi(argv[1]);
int Outport = std::stoi(argv[2]);
//std::cout << "inport = " << argv[1] << " outport = " << argv[2] << std::endl;
std::cout << "inport = " << Inport << " outport = " << Outport << std::endl;
CommuWin com(Inport, Outport);
com.connectTo();
while (true)
{
if (com.GetSendStatus() && com.GetRecvStatus())
{
com.StartReceiveMsg();
break;
}
}
while (true)
{
std::cout << "Enter Send Message" << std::endl;
std::string msg;
std::cin >> msg;
com.sendMsg(msg.c_str());
}
return 0;
}
There are multiple issues with your code. First of all, you need to check results of all the functions, including, but not limited to, to sListen, bind, listen, recv.
NetworkServices::receiveMessage(m_inSocket, message, sizeof(message));
// Without checking recv result there is no way to guess how much
// bytes are actually stored in `message`, if any. Also boldly assuming
// that `message` is null terminated and represents a proper format string
// is dangerous.
printf(message);
You also need to carefully initialize all the stuff, especially sockaddr structures, which may get potentially partially initialized in this case. You are using multiple threads but perform insufficient synchronization. Method sendMsgTo(const char* message) executed by (potentially detached) background thread receives a pointer to a string buffer that may get invalidated at any time.
If you want to send and receive messages, you have to write some code to do that. Nowhere is there any code to send or receive messages. If you think there is, point specifically to the code that figures out whether or not the data you received is one or more messages. You cannot do it.
TCP is not a message protocol. If you need a message protocol on top of TCP, you have to implement one. Have a look at protocols that do this such as HTTP, IRC, or FTP so see how it's done.
If you log the number of bytes received, you will see that all the data you sent was received. It's your job to split that data into messages if you need to -- it won't happen by itself.

What is the proper way to shutdown a ZeroMQ Subscriber?

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

What is a simple example of a working XSUB / XPUB proxy in zeromq

I have a follow-up to How to implement Pub-Sub Network with a Proxy by using XPUB and XSUB in ZeroMQ(C++)?
That question requested a C++ proxy using XSUB and XPUB. The answer given is essentially the proxy main() function quoted below.
I extended this proxy to a full working example including a publisher and subscriber. The catch is that my code only works with dealer / router options (as shown in comments below). With the actual (uncommented) XPUB / XSUB options below, subscribers don't get messages. What's going wrong? Is there a tweak to get messages to arrive?
Proxy not working with XPUB/XSUB (working dealer / router in comments)
#include <zmq.hpp>
int main(int argc, char* argv[]) {
zmq::context_t ctx(1);
zmq::socket_t frontend(ctx, /*ZMQ_ROUTER*/ ZMQ_XSUB);
zmq::socket_t backend(ctx, /*ZMQ_DEALER*/ ZMQ_XPUB);
frontend.bind("tcp://*:5570");
backend.bind("tcp://*:5571");
zmq::proxy(frontend, backend, nullptr);
return 0;
}
Subscriber not working with ZMQ_SUB (working dealer / router option in comments)
#include <iostream>
#include <zmq.hpp>
std::string GetStringFromMessage(const zmq::message_t& msg) {
char* tmp = new char[msg.size()+1];
memcpy(tmp,msg.data(),msg.size());
tmp[msg.size()] = '\0';
std::string rval(tmp);
delete[] tmp;
return rval;
}
int main(int argc, char* argv[]) {
zmq::context_t ctx(1);
zmq::socket_t socket(ctx, /*ZMQ_DEALER*/ ZMQ_SUB);
socket.connect("tcp://localhost:5571");
while (true) {
zmq::message_t identity;
zmq::message_t message;
socket.recv(&identity);
socket.recv(&message);
std::string identityStr(GetStringFromMessage(identity));
std::string messageStr(GetStringFromMessage(message));
std::cout << "Identity: " << identityStr << std::endl;
std::cout << "Message: " << messageStr << std::endl;
}
}
Publisher not working with ZMQ_PUB (working dealer / router option in comments)
#include <unistd.h>
#include <sstream>
#include <zmq.hpp>
int main (int argc, char* argv[])
{
// Context
zmq::context_t ctx(1);
// Create a socket and set its identity attribute
zmq::socket_t socket(ctx, /*ZMQ_DEALER*/ ZMQ_PUB);
char identity[10] = {};
sprintf(identity, "%d", getpid());
socket.setsockopt(ZMQ_IDENTITY, identity, strlen(identity));
socket.connect("tcp://localhost:5570");
// Send some messages
unsigned int counter = 0;
while (true) {
std::ostringstream ss;
ss << "Message #" << counter << " from PID " << getpid();
socket.send(ss.str().c_str(),ss.str().length());
counter++;
sleep(1);
}
return 0;
}
In subscriber code you haven't subscribed to receive messages from the publisher. Try adding the line:
socket.setsockopt(ZMQ_SUBSCRIBE, "", 0);
before/after the line:
socket.connect("tcp://localhost:5571");
in your Subscriber code
broker example
#include <zmq.hpp>
int main(int argc, char* argv[]) {
void* ctx = zmq_ctx_new();
assert(ctx);
void* frontend = zmq_socket(ctx, ZMQ_XSUB);
assert(frontend);
void* backend = zmq_socket(ctx, ZMQ_XPUB);
assert(backend);
int rc = zmq_bind(frontend, "tcp://*:5570");
assert(rc==0);
rc = zmq_bind(backend, "tcp://*:5571");
assert(rc==0);
zmq_proxy_steerable(frontend, backend, nullptr, nullptr);
zmq_close(frontend);
zmq_close(backend);
rc = zmq_ctx_term(ctx);
return 0;
}
pub example
#include <zmq.hpp>
#include <bits/stdc++.h>
using namespace std;
using namespace chrono;
int main(int argc, char* argv[])
{
void* context = zmq_ctx_new();
assert (context);
/* Create a ZMQ_SUB socket */
void *socket = zmq_socket (context, ZMQ_PUB);
assert (socket);
/* Connect it to the host
localhost, port 5571 using a TCP transport */
int rc = zmq_connect (socket, "tcp://localhost:5570");
assert (rc == 0);
while (true)
{
int len = zmq_send(socket, "hello", 5, 0);
cout << "pub len = " << len << endl;
this_thread::sleep_for(milliseconds(1000));
}
}
sub example
#include <iostream>
#include <zmq.hpp>
using namespace std;
int main(int argc, char* argv[])
{
void* context = zmq_ctx_new();
assert (context);
/* Create a ZMQ_SUB socket */
void *socket = zmq_socket (context, ZMQ_SUB);
assert (socket);
/* Connect it to the host localhost, port 5571 using a TCP transport */
int rc = zmq_connect (socket, "tcp://localhost:5571");
assert (rc == 0);
rc = zmq_setsockopt(socket, ZMQ_SUBSCRIBE, "", 0);
assert (rc == 0);
while (true)
{
char buffer[1024] = {0};
int len = zmq_recv(socket, buffer, sizeof(buffer), 0);
cout << "len = " << len << endl;
cout << "buffer = " << buffer << endl;
}
}

ZMQ_CONFLATE does not work for ZMQ_SUB (no filters)

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