QUdpSocket loopback datagram - c++

I'm working with Qt 5.9 and I'm using a bidirectional (send and receive) QUdpSocket.
How can I avoid receiving same message has just sent on the same socket?
Here a snippet of the code
// Socket init
this->UdpSocket->bind( QHostAddress::Any, ARTNET_PROTOCOL_PORT );
connect( this->UdpSocket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()), Qt::UniqueConnection );
[...]
void ArtNetManager::readPendingDatagrams()
{
QNetworkDatagram networkDatagram;
qDebug("Udp datagram received");
while( this->UdpSocket->hasPendingDatagrams() )
{
networkDatagram = this->UdpSocket->receiveDatagram();
qDebug("Received datagram from IP address: %s", networkDatagram.senderAddress().toString().toLatin1().data() );
this->receiveDatagram( networkDatagram.data() );
}
}
void ArtNetManager::sendDatagram()
{
QByteArray ArtNet_RawMsg;
ArtNet_RawMsg.append( "Test program" );
// Writes data on the UDP socket
qint64 sentBytes = this->UdpSocket->writeDatagram( ArtNet_RawMsg, QHostAddress::Broadcast, ARTNET_PROTOCOL_PORT );
if( sentBytes == -1 )
{
qDebug("Cannot send data on UPD socket. Error: %d", this->UdpSocket->error() );
}
else if( sentBytes != ArtNet_RawMsg.size() )
{
qDebug("Wrong number of bytes sent. Bytes sent on socket: %d, tx buffer length: %d", sentBytes, ArtNet_RawMsg.size());
}
}

The messages that you receive are due to the ArtNet behavior. UDP protocol normally doesn't reflect the outgoing traffic back. Some other ArtNet device is doing that.
You could ignore these by keeping a list of a few recently sent messages, and looking them up:
class ArtNetManager : public QObject {
Q_OBJECT
int const m_sentFifoLength = 32;
QList<QByteArray> m_sentFifo;
...
}
void ArtNetManager::sendDatagram() {
sendDatagram({"Test program"});
}
void ArtNetManager::sendDatagram(const QByteArray & msg) {
if (m_sent_fifo.size() >= m_sentFifoLength))
fifo.removeLast();
m_sentFifo.prepend(msg);
auto bytes = m_udpSocket->writeDatagram(msg, QHostAddress::Broadcast, ARTNET_PROTOCOL_PORT);
if (bytes != msg.size())
qWarning() << "can't send datagram" << msg.toHex();
}
void ArtNetManager::readPendingDatagrams() {
while (m_udpSocket->hasPendingDatagrams()) {
auto datagram = m_udpSocket->receiveDatagram();
qDebug() << "received datagram from" << datagram.senderAddress().toString();
receiveDatagram(datagram.data());
}
}
void ArtNetManager::receiveDatagram(const QByteArray & msg) {
auto it = std::find(m_sentFifo.begin(), m_sentFifo.end(), msg);
if (it != m_sentFifo.end()) {
m_sentFifo.erase(it);
return;
}
...
}

Related

C++ TCP Proxy ASIO missing packets

I am building a small application for my company. I will spare you on the details why, but I was requested to build a TCP Proxy for a specific application and protocol that we have.
For this Proxy, it would make a TCP connection to a server that would send us data periodically, and the Proxy would mirror the data received to two other applications on different ports. For this, I used C++ and ASIO library. I will spare you the full code, but here is (what I think) the important part regarding TCP Connections:
The class belowhandles the incoming data received by our Proxy. Data received here should be sent to our clients.
#include "TCPInConnection.h"
namespace Proxy
{
TCPInConnection::TCPInConnection(asio::io_context& io_context, std::string host, int port) :
host(host),
port(port),
io_context(&io_context),
socket(io_context)
{
}
TCPInConnection::~TCPInConnection()
{
Close();
}
//Starts connection to the server
void TCPInConnection::Start()
{
if(!socket.is_open())
connect();
}
void TCPInConnection::Close()
{
if (socket.is_open())
socket.close();
}
//Receives a callback. The callback passed should be called everytime a new packet was received
void TCPInConnection::OnReceive(std::function<std::vector<uint8_t>(std::vector<uint8_t>&)> callback)
{
this->callback = callback;
}
//Handles the connection. If an error occured while connecting, try connection again. When connecting, call receive handler to read data received
void TCPInConnection::connect()
{
socket.async_connect(asio::ip::tcp::endpoint(asio::ip::address::from_string(host), port), [this](const asio::error_code& error) {
if (error)
{
this->connect();
return;
}
asio::socket_base::receive_buffer_size option(8192 * 40);
socket.set_option(option);
receive();
});
}
//Handles the reception of data. For evey incoming packet, it should call itself to receive the next packet. If socket is closed, try to reconnect.
void TCPInConnection::receive()
{
std::array<char, 4000>* receiveBuffer = new std::array<char, 4000>();
socket.async_receive(asio::buffer(*receiveBuffer), 0, [this, receiveBuffer](const asio::error_code& error, std::size_t len) {
if (error == asio::error::eof || error == asio::error::connection_reset)
{
if (this->socket.is_open())
this->socket.close();
connect();
delete receiveBuffer;
return;
}
receive();
if (len == 0)
{
delete receiveBuffer;
return;
}
std::vector<uint8_t> packet(receiveBuffer->begin(), receiveBuffer->begin() + len);
auto data = callback(packet);
delete receiveBuffer;
if (data.size() == 0)
return;
std::vector<uint8_t>* output = new std::vector<uint8_t>(data.begin(), data.end());
socket.async_write_some(asio::buffer(*output), [output](const asio::error_code& error, std::size_t bytes_transferred) {
delete output;
});
});
}
}
The next class handles the output to our clients:
#include "TCPOutConnection.h"
namespace Proxy
{
TCPOutConnection::TCPOutConnection(asio::io_context& io_context, int port) :
io_context(&io_context),
acceptor(io_context, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port)),
clientsMutex(),
clients()
{
}
TCPOutConnection::~TCPOutConnection()
{
Close();
}
//Starts listening for clients
void TCPOutConnection::Start()
{
connect();
}
void TCPOutConnection::Close()
{
std::lock_guard<std::mutex> guard(closingMutex);
if (acceptor.is_open())
acceptor.close();
{
std::lock_guard<std::mutex> guard(clientsMutex);
for (auto c = clients.begin(); c != clients.end(); ++c)
{
asio::ip::tcp::socket* socket = *c;
socket->close();
delete socket;
}
clients.clear();
}
}
//Sends data to each of the clients.
void TCPOutConnection::Send(std::vector<uint8_t> data)
{
if (data.size() == 0)
return;
std::lock_guard<std::mutex> guard(clientsMutex);
for (auto& socket : clients)
{
std::vector<uint8_t>* dataToSend = new std::vector<uint8_t>(data.begin(), data.end());
socket->async_send(asio::buffer(*dataToSend), [socket, this, dataToSend](const asio::error_code& error, std::size_t bytes_transferred) {
delete dataToSend;
if (!error)
return;
std::lock_guard<std::mutex> guard(clientsMutex);
clients.remove(socket);
});
}
}~
//Still not used. Would be implemented later.
void TCPOutConnection::OnDataReceived(std::function<void(std::vector<uint8_t>)> callback)
{
}
//Handles connection of new clients. Everytime a client comes, it should listen for another one
void TCPOutConnection::connect()
{
asio::ip::tcp::socket* socket = new asio::ip::tcp::socket(*io_context);
acceptor.async_accept(*socket, [socket, this](const asio::error_code& error) {
connect();
if (error)
{
delete socket;
return;
}
asio::ip::tcp::no_delay option1;
socket->set_option(option1);
asio::socket_base::receive_buffer_size option2(8192 * 40);
socket->set_option(option2);
std::lock_guard<std::mutex> guard(clientsMutex);
clients.push_back(socket);
});
}
}
Although the above works fine, I am losing some packets when our server is sending too much packets to the proxy (~15 packets per millisecond).
Is there anything I could do to avoid losing packets?

boost::asio::async_read loops infinite with zero byte of received data

I am trying to write IO_Service based async TCP client where Async_write works fine but async_read runs in infinite loop . During my trial to rectify the issue i found that in all the other cases async_read just stops receiving data , it receives nothing until i stop the server. Below is the code and the links which i tried before posting my query.
The suggestions i tried are exactly as mine , 2 , 3 and but in all the cases my async_read handler does not read anything . In one and only one case it start infinite loop when i set buffer as boost::asio::mutable_buffer bytes; in rest cases i have tried boost::array<char, 512> bytes; , boost::asio::streambuf bytes; and char bytes[512]; where the async_read handler is not raised.
After going through all of these solution I am now confused : can it be the problem of buffer ? Do I need to initialize it before passing to read
?
Please guide.
ScalableSocket::ScalableSocket()
{
//ctor
using namespace boost::asio;
service = boost::make_shared<io_service>();
work = boost::make_shared<io_service::work>(*service );
strand = boost::make_shared<io_service::strand>( *service );
worker_threads = boost::make_shared<boost::thread_group>();
worker_threads->create_thread(boost::bind(&ScalableSocket::WorkerThread,this));
resolver = boost::make_shared<boost::asio::ip::tcp::resolver> (*service);
tcp_socket= boost::make_shared<boost::asio::ip::tcp::socket> (*service);
boost::asio::ip::tcp::resolver::query q(boost::asio::ip::tcp::v4(),"192.168.100.96","9602");
boost::asio::ip::tcp::resolver::iterator it = resolver->resolve(q);
boost::asio::async_connect(*tcp_socket,it,boost::bind(&ScalableSocket::connect_handler,this,boost::asio::placeholders::error));
tcp_socket->set_option(boost::asio::ip::tcp::no_delay(true) );
}
ScalableSocket::~ScalableSocket()
{
//dtor
}
void ScalableSocket::PublishPost()
{
strand->post(boost::bind(&ScalableSocket::OnSend,this));
}
void ScalableSocket::OnSend()
{
boost::array<char, 6> a = { 'a', 'b', 'c', 'd', 'e' };
boost::asio::async_write(*tcp_socket,boost::asio::buffer(a),
boost::bind(&ScalableSocket::write_handler, this, boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));
}
void ScalableSocket::WorkerThread()
{
while( true )
{
try
{
boost::system::error_code ec;
service->run( ec );
if( ec )
{
///LOGE(ec);
}
break;
}
catch( std::exception & ex )
{
///LOGE(ex.what());
}
}
}
void ScalableSocket::connect_handler(const boost::system::error_code &ec)
{
if (!ec)
{
PublishPost();
/* boost::asio::async_read(*tcp_socket,
boost::asio::buffer(bytes),
boost::bind(&ScalableSocket::read_handler, this,
boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));
*/
///https://stackoverflow.com/questions/4527443/problems-using-boostasioasync-read
boost::shared_ptr<boost::array<char, 512>> buf(new boost::array<char, 512>);
boost::asio::async_read(*tcp_socket,boost::asio::buffer(*buf),
boost::bind(&ScalableSocket::read_handler, this,buf,
boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));
}
else
{
cout<<" Some error connecting to Exchange "<< ec.message()<<endl;
}
}
void ScalableSocket::OnTimer(const boost::system::error_code &ec)
{
if(!ec)
{
printf("\n\n Heartbeat event raised sending KeepAlive to exchange \n\n");
PublishPost();
HeartBeatTimer->async_wait(boost::bind(&ScalableSocket::OnTimer,this, boost::asio::placeholders::error));
}
}
void ScalableSocket::recvData()
{
boost::system::error_code error;
boost::array<char, 1024> buf;
//for(;;)
{
size_t len = tcp_socket->read_some(boost::asio::buffer(buf), error);
cout<<"\n Recv data size is "<<len;
}
}
void ScalableSocket::read_handler(boost::shared_ptr<boost::array<char, 512>> buf,const boost::system::error_code &ec,std::size_t bytes_transferred)
{
if (!ec )//&& bytes_transferred > 0)
{
///recvData(); /// If i enable this code during infinite loop it start getting data that means socket has no issue
cout << " Data size recieved "<< bytes_transferred<<endl;
boost::asio::async_read(*tcp_socket,boost::asio::buffer(*buf),
boost::bind(&ScalableSocket::read_handler, this,buf,
boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));
}
else
{
/// Some issue with socket publish error , inform user and reconnect
cout<<" Some error reading data from Exchange "<< ec.message()<<endl;
}
}
void ScalableSocket::write_handler(const boost::system::error_code& error,std::size_t bytes_transferred)
{
if(!error)
{
/// data Sent successfully
cout<< " Data sent size "<< bytes_transferred<<endl;
}
else
{
cout<<" Some error sending data to Exchange "<< error.message()<<endl;
}
}
asnyc_readwill NOT "return" / call the handler until the given buffer is completely full.
asnyc_read_some will return after some bytes have been read. This might be the function you're looking for.
Remember to handle received data correctly with asnyc_read_some. If you send 512 bytes, it may arrive in a couple of reads, depending on the machine.

Can not send udp packet in QT

all
I want to use QUdpSocket to send udp packet to get config parameter from specific server,but It failed and I can not capture the sending packet using wireshark.here is my code:
CGetConfig.cpp:
CGetConfig::CGetConfig(const QString &conf_server,const uint16_t port)
:m_conf_server(conf_server)
,m_port(port)
{
m_socket = NULL;
}
CGetConfig::~CGetConfig()
{}
void CGetConfig::init()
{
// create a QUDP socket
m_socket = new QUdpSocket(this);
m_socket->bind(QHostAddress::LocalHost, 12345);
connect(m_socket, SIGNAL(readyRead()), this, SLOT(readyRead()));
m_ip_addr = get_ip_address(m_conf_server);
}
bool CGetConfig::get_reflector(const QString &mac)
{
qDebug() << "CGetConfig::get_reflector():Entry\n";
if(m_ip_addr.isEmpty())
{
qDebug() << "CGetConfig::get_reflector():ip address of cofnig server could not be resolved\n";
return 0;
}
QString msg("id=1&mac=");
msg+= mac;
msg+= "&get_config=fw_type=v.1,cfg_ver=4,set_ver=0,ip=192.168.1.101";
qDebug() << m_ip_addr;
qDebug() << m_port;
qDebug() << msg.toLatin1();
int count = 0;
while(count < 3)
{
int t = m_socket->writeDatagram(msg.toLatin1(), QHostAddress(m_ip_addr), m_port);
count++;
}
}
Main.cpp
CGetConfig cfg(cfg_server,cfg_port);
cfg.init();
local_mac = "00d033120001";
cfg.get_reflector(local_mac);
Can anyone help me figure out the problem?

Qt, Sending multiple data types from client to server + data Streaming

I have a Client/Server based Qt application, using QTcpServer and QTcpSocket, I managed to do the connection and send some data back and forth between the client and the server.
The client sends many types of data to the server (string, int, files and a real time audio stream) and since my server impliment a single data input SLOT (readyRead()):
connect(socket, SIGNAL(readyRead()),this, SLOT(readyRead()));
I don't know how could I distinguish between all this received data and call respectively the right function in the server.
Example (in the server):
- if I receive string => call function showData(QString data);
- if I receive file => call function saveFile(QFile file);
- if I receive audio stream => play audio stream
- ...
SERVER:
void Server::newClientConnection()
{
QTcpSocket *socket = server->nextPendingConnection();
connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()));
//...
}
void Server::readyRead()
{
QTcpSocket *clientSocket = qobject_cast<QTcpSocket *>(sender());
if (clientSocket == 0) {
return;
}
QDataStream in(clientSocket);
if (sizeMessageClient == 0)
{
if (clientSocket->bytesAvailable() < (int)sizeof(quint16)){
return;
}
in >> sizeMessageClient;
}
if (clientSocket->bytesAvailable() < sizeMessageClient) {
return;
}
sizeMessageClient = 0;
in >> data;
/*
I don't know the type of the received data !!
- if I receive string => call function showData(QString data);
- if I receive file => call function saveFile(QFile file);
- if I receive audio stream => play audio stream
- ...
*/
}
CLIENT:
Client::Client()
{
socket = new QTcpSocket(this);
connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()));
sizeMessageServer = 0;
}
void Client::readyRead()
{
QDataStream in(socket);
if (sizeMessageServer == 0)
{
if (socket->bytesAvailable() < (int)sizeof(quint16)) {
return;
}
in >> sizeMessageServer;
}
if (socket->bytesAvailable() < sizeMessageServer) {
return;
}
int messageReceived;
in >> messageReceived;
messageReceived = static_cast<int>(messageReceived);
sizeMessageServer = 0;
switch(messageReceived)
{
case 1:
qDebug() << "send a File";
sendFile();
break;
case 2:
qDebug() << "send a string data";
sendStringData();
break;
case 3:
qDebug() << "stream audio to the server";
streamAudioToServer();
break;
case n:
// ...
}
}
I am not looking for a complete solution, all I am looking for is some guidance in the right direction.
The implementation of your protocol doesn't fully leverage QDataStream in Qt 5.7. Here's how it might look now - it can be quite simple.
First, let's define the requests we know of:
enum class Req : quint32 {
Unknown, String, File
};
Q_DECLARE_METATYPE(Req)
QDataStream & operator<<(QDataStream & ds, Req req) {
return ds << (quint32)req;
}
QDataStream & operator>>(QDataStream & ds, Req & req) {
quint32 val;
ds >> val;
if (ds.status() == QDataStream::Ok)
req = Req(val);
return ds;
}
It'd also be handy to have a transaction RAII helper.
struct Transaction {
QDataStream & stream;
Transaction(QDataStream & stream) : stream{stream} {
stream.startTransaction();
}
~Transaction() {
stream.commitTransaction();
}
bool ok() {
return stream.status() == QDataStream::Ok;
}
};
The client receives requests from the server and signals the need to reply with data. The code that uses the client would react to these signals and reply back by invoking a matching slot. E.g.
void clientUser(Client & client) {
QObject::connect(&client, &Client::needString, &client, [&]{
client.sendString(QStringLiteral{"You got string!"});
});
And:
class Client : public QObject {
Q_OBJECT
QIODevice & m_dev;
QDataStream m_str{&m_dev};
void onReadyRead() {
Transaction tr{m_str};
Req req;
m_str >> req;
if (!tr.ok()) return;
if (req == Req::String)
emit needString();
else if (req == Req::File) {
QString fileName;
m_str >> fileName;
if (!tr.ok()) return;
emit needFile(fileName);
}
else emit unknownRequest(req);
}
public:
Client(QIODevice & dev) : m_dev{dev} {
connect(&m_dev, &QIODevice::readyRead, this, &Client::onReadyRead);
}
Q_SIGNAL void unknownRequest(Req);
Q_SIGNAL void needString();
Q_SIGNAL void needFile(const QString & fileName);
Q_SLOT void sendString(const QString & str) {
m_str << Req::String << str;
}
Q_SLOT void sendFile(const QString & fileName, const QByteArray & data) {
m_str << Req::File << fileName << data;
}
};
The server is very similar. Its user sends the request to a client via request slots. Once the server hears back from the client, it indicates it through the has signals:
class Server : public QObject {
Q_OBJECT
QIODevice & m_dev;
QDataStream m_str{&m_dev};
void onReadyRead() {
Transaction tr{m_str};
Req req;
m_str >> req;
if (!tr.ok()) return;
if (req == Req::String) {
QString str;
m_str >> str;
if (!tr.ok()) return;
emit hasString(str);
}
else if (req == Req::File) {
QString fileName;
QByteArray data;
m_str >> fileName >> data;
if (!tr.ok()) return;
emit hasFile(fileName, data);
}
else emit hasUnknownRequest(req);
}
public:
Server(QIODevice & dev) : m_dev{dev} {
connect(&m_dev, &QIODevice::readyRead, this, &Server::onReadyRead);
}
Q_SIGNAL void hasUnknownRequest(Req);
Q_SIGNAL void hasString(const QString &);
Q_SIGNAL void hasFile(const QString & name, const QByteArray &);
Q_SLOT void requestString() {
m_str << Req::String;
}
Q_SLOT void requestFile(const QString & name) {
m_str << Req::File << name;
}
};

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