Qt with ZeroMQ publish subscribe pattern - c++

I would like to use ZeroMQ(4.1.2) with Qt (5.2.1).
Idea is to have zmq pub/sub (where server is outside) and sub is qt app.
Currently receive in Qt app runs once, could someone drop hint?
Should ZeroMQ receiver be implemented in some other way?
Currently my code looks like:
mainwindow.h
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_pushButton_clicked();
void readZMQData();
private:
Ui::MainWindow *ui;
QSocketNotifier *qsn;
void *context;
void *subscriber;
};
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
/***** ZMQ *****/
context = zmq_ctx_new ();
subscriber = zmq_socket (context, ZMQ_SUB);
int rc = zmq_connect (subscriber, "tcp://localhost:5556");
char *filter = "";
rc = zmq_setsockopt (subscriber, ZMQ_SUBSCRIBE,filter, strlen (filter));
unsigned int fd=0;
size_t fd_size = sizeof(fd);
rc = zmq_getsockopt(subscriber,ZMQ_FD,&fd,&fd_size);
qsn = new QSocketNotifier(fd, QSocketNotifier::Read, this);
connect(qsn, SIGNAL(activated(int)), this, SLOT(readZMQData()), Qt::DirectConnection);
}
MainWindow::~MainWindow()
{
zmq_close (this->subscriber);
zmq_ctx_destroy (this->context);
delete ui;
}
void MainWindow::readZMQData()
{
qsn->setEnabled(false);
qDebug() << "Got data!";
int events = 0;
std::size_t eventsSize = sizeof(events);
zmq_getsockopt(subscriber,ZMQ_EVENTS, &events, &eventsSize);
if(events & ZMQ_POLLIN){
qDebug() << " ====== Data to read ======";
char *string = s_recv(subscriber);
qDebug() << "DATA: " << string;
free(string);
}
qsn->setEnabled(true);
}
And server app is (from ZeroMQ examples):
#include "zhelpers.h"
int main (void)
{
// Prepare our context and publisher
void *context = zmq_ctx_new ();
void *publisher = zmq_socket (context, ZMQ_PUB);
int rc = zmq_bind (publisher, "tcp://*:5556");
assert (rc == 0);
// Initialize random number generator
srandom ((unsigned) time (NULL));
while (1) {
// Get values that will fool the boss
int zipcode, temperature, relhumidity;
zipcode = randof (100000);
temperature = randof (215) - 80;
relhumidity = randof (50) + 10;
// Send message to all subscribers
char update [20];
sprintf (update, "%05d %d %d", zipcode, temperature, relhumidity);
s_send (publisher, update);
}
zmq_close (publisher);
zmq_ctx_destroy (context);
return 0;
}

First tnx for helping out,
I've found the issue, when ZeroMQ notifies that there is message to read you need to read them all, not just first one.
void MainWindow::readZMQData(int fd)
{
qsn->setEnabled(false);
int events = 0;
std::size_t eventsSize = sizeof(events);
zmq_getsockopt(subscriber,ZMQ_EVENTS, &events, &eventsSize);
if(events & ZMQ_POLLIN){
qDebug() << " ====== Data to read ======";
char *string;
// THIS IS THE TRICK! READ UNTIL THERE IS MSG
while((string = s_recv_nb(subscriber)) != NULL){
qDebug() << "DATA: " << string;
free(string);
}
}
qsn->setEnabled(true);
}

The socket notifier looks like it should work. Have you read the docs on handling it properly? Especially if you are on Windows, it looks like there are special ways to handle it when doing the read... disabling, reading, etc.
http://doc.qt.io/qt-5/qsocketnotifier.html#details
Hope that helps.

As it is not clear what s_recv_nb(zmq::socket_t & socket) is, I'll provide my - slightly more detailed - implemetation of:
void MainWindow::readZMQData()
{
qsn->setEnabled(false);
int events = 0;
std::size_t eventsSize = sizeof(events);
zmq_getsockopt(subscriber,ZMQ_EVENTS, &events, &eventsSize);
if(events & ZMQ_POLLIN){
qDebug() << " ====== Data to read ======";
char *string;
int64_t more;
size_t more_size = sizeof (more);
do {
/* Create an empty ØMQ message to hold the message part */
zmq_msg_t part;
int rc = zmq_msg_init (&part);
assert (rc == 0);
rc = zmq_msg_recv (&part, subscriber, 0);
assert (rc != -1);
/* Determine if more message parts are to follow */
rc = zmq_getsockopt (subscriber, ZMQ_RCVMORE, &more, &more_size);
assert (rc == 0);
string = (char*) zmq_msg_data(&part);
qDebug() << QString(string) ; // << "more" << more;
zmq_msg_close (&part);
} while (more);
}
qsn->setEnabled(true);
}
And one more remark: in case of Windows 64 the filedescriptor fdshould be uint64_t as in zmq_getsockopt returns EINVAL on windows x64 when local address of ZMQ_FD option_val passed

Related

C++ QT QAudioInput record files different lenght

My program is reading a configuration-file lines in a loop, and do a audio-recording for time = lines*100ms.
But for 5 same config-files I get 5 different length. (For 5sek recordings +-100ms)
I trying to change QThread::msleep(1500); or to declare QEventLoop and QTimer outside a loop, but there are no changes.
How can I get the recordings with right times?
Audio.cpp
// ************************************************************************************************
// Audio-Class
// ************************************************************************************************
#include "Audio.h"
#include "Measure.h"
#include <QAudioInput>
#include <QTimer>
// ************************************************************************************************
Audio::Audio()
{
QTimer *m_timer = new QTimer(this);
m_timer->setSingleShot(true);
AudioRecord();
}
// ************************************************************************************************
//Initialization and signal-slot connection
void Audio::AudioRecord()
{
//Set audio configuration
QAudioFormat format;
format.setSampleRate(44100);
format.setChannelCount(1);
format.setSampleSize(16);
format.setCodec("audio/PCM");
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::SignedInt);
//Wrong configuration detection
QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();
if (!info.isFormatSupported(format))
{
qWarning() << "Default format not supported";
format = info.nearestFormat(format);
}
//Signal-slot connection to show actual Audio state or errors output
m_audio = new QAudioInput(format, this);
connect(m_audio, SIGNAL(stateChanged(QAudio::State)), this, SLOT(handleStateChanged(QAudio::State)));
}
// ************************************************************************************************
//Start recording
//void Audio::StartRecording(QString rec_file_path)
void Audio::StartRecording(QString rec_file_path, quint32 record_time_msec)
{
m_file.setFileName(rec_file_path); //audio recording path format
m_file.open(QIODevice::WriteOnly); //audio access mode initialisation
writeHeader(); //writing header to convert PCM to *.wav (Step 1)
m_audio->start(&m_file); //start recording
//QTimer::singleShot((15000), this, SLOT(StopRecording()));
QTimer::singleShot((record_time_msec), this, SLOT(StopRecording()));
QCoreApplication::processEvents();
qDebug()<< m_audio->format();
//qDebug()<< m_file.open();
QAudioDeviceInfo::defaultInputDevice();
QAudioDeviceInfo::availableDevices(QAudio::AudioInput);
//qDebug()<< m_audio->error();
//qDebug()<< m_audio->notifyInterval();
}
// ************************************************************************************************
//Write Header
// ************************************************************************************************
void Audio::writeHeader(){
quint64 data64 = 0;
quint32 data_Filesize_fill = 0;
quint32 data_lenght = 16;
quint16 data_PCM = 1;
quint16 data_Chanel = 1;
quint32 data_SamplRate = 44100;
quint32 data_Value = (44100 * 16 * 1)/8;
quint16 data_mono = (16 * 1)/8;
quint16 data_BPS = 16;
quint32 data_FileSize = 44;
m_file.write("RIFF",4);
m_file.write((char*)&data_Filesize_fill, 4);
m_file.write("WAVE",4);
m_file.write("fmt ",4); // "fmt " chunk size (always 16 for PCM)
m_file.write((char*)&data_lenght, 4);
m_file.write((char*)&data_PCM, 2);
m_file.write((char*)&data_Chanel, 2);
m_file.write((char*)&data_SamplRate, 4);
m_file.write((char*)&data_Value, 4); // bytes per second
m_file.write((char*)&data_mono, 2); // Block align
m_file.write((char*)&data_BPS, 2); // Bits per sample
m_file.write("data",4);
m_file.write((char*)&data_FileSize, 4);
m_file.flush();
}
// ************************************************************************************************
//Stop recording
void Audio::StopRecording()
{
m_audio->stop();
qDebug() << "stop";
writeHeaderFinal();
m_file.close();
qDebug() << "close";
}
// ************************************************************************************************
//Write Header
void Audio::writeHeaderFinal(){
quint32 data_Filesize_fill_after = m_file.size() - 8;
quint32 data_FileSize_after = m_file.size() - 44;
m_file.seek(4);
m_file.write((char*)&data_Filesize_fill_after, 4);
m_file.seek(40);
m_file.write((char*)&data_FileSize_after, 4);
}
// ************************************************************************************************
//Recording DEBUG output
void Audio::handleStateChanged(QAudio::State newState)
{
switch (newState)
{
case QAudio::StoppedState:
if (m_audio->error() != QAudio::NoError)
{
qDebug() << "Error audio recording!!";
} else
{
qDebug() << "Finished audio recording";
}
break;
case QAudio::ActiveState:
qDebug() << "Started audio recording";
break;
default:
break;
}
}
// ************************************************************************************************
Measure.cpp
// ************************************************************************************************
// Measure-Class
// ************************************************************************************************
//class initialisation
#include "Measure.h"
#include "Conf.h"
#include "Audio.h"
// ************************************************************************************************
Measure::Measure(Audio *audio)
{
//member variables init
m_audio = audio;
//UART-status debug
if (0 == connectSerial())
{
qDebug() << "Serial connection OK!\n";
}
else
{
qDebug() << "Serial connection Error!\n";
return;
}
auto info = QAudioDeviceInfo::availableDevices(QAudio::AudioInput);
foreach (auto i, info) qDebug() << i.deviceName();
//m_timer = new QTimer(this); double
//set serial connection
//connect(m_timer, SIGNAL(timeout()), this, SLOT(processMeas()),Qt::QueuedConnection);
measure_cnt = 0;
qDebug()<<"Starting programm";
}
// ************************************************************************************************
// Start measure
void Measure::start()
{
//QTimer *m_timer = new QTimer(this);
//m_timer->setSingleShot(true);
//m_timer->start(1000); 555
processMeas();
}
// ************************************************************************************************
// Serial connection initialization and signal-slot connection
uint32_t Measure::connectSerial(void)
{
//serial connection configuration
m_serial.setPortName("/dev/ttyACM0");
m_serial.setBaudRate(QSerialPort::Baud115200);
m_serial.setDataBits(QSerialPort::Data8);
m_serial.setParity(QSerialPort::NoParity);
m_serial.setStopBits(QSerialPort::OneStop);
m_serial.setFlowControl(QSerialPort::NoFlowControl);
if (!m_serial.open(QIODevice::ReadWrite))
return 1;
//einable serial connection
connect(&m_serial, SIGNAL(readyRead()), this, SLOT(readRequest()));
return 0;
}
// ************************************************************************************************
// State maschine
// ************************************************************************************************
void Measure::processMeas()
{
// Declaration ************************************************************
static uint32_t i = 0;
static uint32_t j = 0;
static uint8_t stm = 0;
static uint16_t m1 = 0;
static uint16_t m2 = 0;
static uint16_t m3 = 0;
static uint16_t m4 = 0;
static uint16_t var;
static QByteArray hextest;
// Read config files ***********************************************************************************
QDir directory("/home/nikitajarocky/workspace/QT/Software_2.0_QT/Config_files/");
QStringList config_files = directory.entryList(QStringList() << "*.config", QDir::Files);
config_files_cnt = config_files.count();
foreach(QString filename, config_files)
{
Conf *conf = new Conf(directory.absoluteFilePath(filename));
qDebug() << "Config file: " << filename << " is processed";
// Error message *************************
if(conf->getConf_lines().size() == 0)
{
qDebug()<<"Error conf file!!";
return;
}
// Prepare measure ****************************************************************
qDebug()<<"Debug: Prepare measure";
QStringList lines = conf->getConf_lines(); //read all rows of config files
record_time_100ms = lines.count()-9; // lines Audio config data + end lines
record_time_msec = record_time_100ms * 100; //msec conversion
for(i=0; i < (uint32_t)lines.length(); i++) //prepare single rows of config files
{
//uint32_t j = 0;
QString uart;
QString uart_hex;
if(i > 5 && i < lines.length())
{
QStringList speed_chunks = lines.at(i).split(","); //split rows by decimal point
m1 = speed_chunks.at(2).toInt();
m2 = speed_chunks.at(3).toInt();
m3 = speed_chunks.at(4).toInt();
m4 = speed_chunks.at(5).toInt();
hextest.resize(10);
hextest[0]=255;
hextest[1]=m1>>8;
hextest[2]=m1;
hextest[3]=m2>>8;
hextest[4]=m2;
hextest[5]=m3>>8;
hextest[6]=m3;
hextest[7]=m4>>8;
hextest[8]=m4;
hextest[9]=238;
qDebug()<< "Transfer data: " << i-8 << " from " << lines.count()-9 << " rows transmitted";
qDebug() << "Config file: " << filename << " is processed";
m_serial.write(hextest);
m_serial.waitForBytesWritten(1000);
QThread::usleep(50000);
}//if(lines.at(i).contains(",")) end }//for end
}//for end
//Beep
// Measurement ******************************************************************
qDebug()<<"Debug: Measurement in progress";
QEventLoop event_loop(this); //create method class QEventLoop
QTimer exit_timer(this); //create method class QTimer
exit_timer.setSingleShot(true); //set timer in mode singleShot
connect(&exit_timer, SIGNAL(timeout()), &event_loop, SLOT(quit())); //connect exit timer to event loop slot
QThread::msleep(1500);
QString path = SAVE_AUDIO_PATH+filename.left( filename.lastIndexOf( '.' ) )+"_"+QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss");
//QThread::msleep(1500);
m_file.setFileName(path+".log");
//m_audio->StartRecording(path+".wav");
m_audio->StartRecording(path+".wav", record_time_msec);//+0.6
//exit_timer.start(15000);
exit_timer.start(record_time_msec);//event loop timer durring measure time
event_loop.exec();
qDebug()<<"Measurement takes " << record_time_msec << " seconds";
// Stop measure ************************************
QThread::msleep(2000);
stm=0;
record_time_msec = 0;
measure_cnt++;
// All configs files done *********************************
if(measure_cnt>=config_files_cnt)
{
qDebug()<<"All measure done";
qApp->quit();
}
}//foreach
}//processMeas()
// ************************************************************************************************
// Data from STM32 receive
void Measure::readRequest()
{
m_file.open(QIODevice::WriteOnly | QIODevice::Append);
m_file.write(m_serial.readAll());
m_file.close();
}
// ************************************************************************************************
// save received data to log File
void Measure::saveFile(QByteArray buffer)
{
m_file.write(buffer);
}
Recordings are always little bit longer, so its a not stochastic error :
On the picture are 4 same config-files, two time
EDIT:
I change a code to:
m_audio->StartRecording(path+".wav", record_time_msec+5000);
exit_timer.start(record_time_msec+5000);//event loop timer
event_loop.exec();
an get:
But how can I start my measure delayed? (I want to skip beginning Sound (Buzzer))?

Handeling large QByteArray with QtAudioOutput causes std::bad_alloc

Using QAudioOut Im trying to play data that is stored in a QByteArray in a sequence... this works when there is little data being appended, however when the data gets too much, lets say a 2~3 hour RAW PCM appending from different combinations this data to a QByteArray all at once will result in a std::bad_alloc due to the heap that's not big enough to hold all the data at the same time.
I know where the problem occurs and I think I have a possible solution, its just I have no idea on how to go about implementing it.
Below is a converted function that takes the values in the list
first one 440Hz for 1800000 msec and created RAW PCM square wave. Which works then appends it to a QByteArray then plays it.
This will work if there isn't a lot of appended data form multiple added sequences.
Im looking for a way to do one at a time from the list then create the wave, play that one for x milliseconds then move on to the next entry in the MySeq list. The list can contain large sequences of 3 minute frequencies that runs for hours.
QStringList MySeq;
MySeq << "1:440:180000";
MySeq << "1:20:180000";
MySeq << "1:2120:180000";
MySeq << "1:240:180000";
MySeq << "1:570:180000";
foreach(QString seq, MySeq)
{
QStringList Assits = seq.split(":");
qDebug() << "Now At: " << seq;
QString A = Assits.at(0);
QString B = Assits.at(1);
QString C = Assits.at(2);
qreal amplitude = A.toInt();
float frequency = B.toFloat();
int msecs = C.toInt();
qreal singleWaveTime = amplitude / frequency;
qreal samplesPerWave = qCeil(format->sampleRate() * singleWaveTime);
quint32 waveCount = qCeil(msecs / (singleWaveTime * 1000.0));
quint32 sampleSize = static_cast<quint32>(format->sampleSize() / 8.0);
QByteArray data(waveCount * samplesPerWave * sampleSize * format->channelCount(), '\0');
unsigned char* dataPointer = reinterpret_cast<unsigned char*>(data.data());
for (quint32 currentWave = 0; currentWave < waveCount; currentWave++)
{
for (int currentSample = 0; currentSample < samplesPerWave; currentSample++)
{
double nextRadStep = (currentSample / static_cast<double>(samplesPerWave)) * (2 * M_PI);
quint16 sampleValue = static_cast<quint16>((qSin(nextRadStep) > 0 ? 1 : -1) * 32767);
for (int channel = 0; channel < format->channelCount(); channel++)
{
qToLittleEndian(sampleValue, dataPointer);
dataPointer += sampleSize;
}
}
}
soundBuffer->append(data); // HERE IS THE Std::Bad_Alloc
output->start(outputBuffer);
qDebug() << data.size()
}
I wish to fill the QByteArray with only one sequence at a time then Play it with QAudioOutput then clear the ByteArray then load the next sequence repeat until all sequences are done in the QStringList.
The problem with this approach now is that QAudioOutput is asynchronous and doesn't wait for the first sequence to finish
If I loop over the list as demonstrated above they load one after the other with only the last frequency actually playing. its like the loop keep overwriting the previous sequence.
Im not sure if QEventLoop (Something I haven't used yet) is needed here or Threading. I have tried a couple of different approaches with no success. Any advice would be greatly appreciated. This is a continuation of the following question I had about wave files and data and frequency generations located here
Qt C++ Creating a square audio tone wave. Play and saving it
mainWindows.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtCore>
#include <QtMultimedia/QAudioOutput>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void playbackFinished();
private slots:
void appendSound();
void on_pushButton_Run_clicked();
void on_pushButton_Stop_clicked();
private:
Ui::MainWindow *ui;
QByteArray *soundBuffer;
QBuffer *outputBuffer;
QAudioFormat *format;
QAudioOutput *output;
};
#endif // MAINWINDOW_H
mainWindows.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
int sampleRate = 44100;
int channelCount = 2;
int sampleSize = 16;
const QString codec = "audio/pcm";
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
soundBuffer = new QByteArray();
format = new QAudioFormat();
format->setSampleRate(sampleRate);
format->setChannelCount(channelCount);
format->setSampleSize(sampleSize);
format->setCodec(codec);
format->setByteOrder(QAudioFormat::LittleEndian);
format->setSampleType(QAudioFormat::UnSignedInt);
output = new QAudioOutput(*format, this);
connect(output, SIGNAL(stateChanged(QAudio::State)), this, SLOT(playbackFinished()));
outputBuffer = new QBuffer(soundBuffer);
if (outputBuffer->open(QIODevice::ReadOnly) == false) {
qCritical() << "Invalid operation while opening QBuffer. audio/pcm";
return;
}
}
MainWindow::~MainWindow()
{
delete ui;
delete soundBuffer;
delete format;
delete output;
delete outputBuffer;
}
void MainWindow::playbackFinished()
{
if (output->state() == QAudio::IdleState)
{
qDebug() << "Playback finished";
}
}
void MainWindow::appendSound()
{
QStringList MySq;
MySq << "1:440:180000";
MySq << "1:20:180000";
MySq << "1:2120:180000";
MySq << "1:240:180000";
MySq << "1:570:180000";
MySq << "1:570:180000";
MySq << "1:570:180000";
MySq << "1:850:180000";
MySq << "1:1570:180000";
MySq << "1:200:180000";
MySq << "1:50:180000";
MySq << "1:85:180000";
MySq << "1:59:180000";
MySq << "1:20:180000";
foreach(QString seq, MySq)
{
QStringList Assits = seq.split(":");
qDebug() << "Now At: " << seq;
QString A = Assits.at(0);
QString B = Assits.at(1);
QString C = Assits.at(2);
qreal amplitude = A.toInt();
float frequency = B.toFloat();
int msecs = C.toInt();
msecs = (msecs < 50) ? 50 : msecs;
qreal singleWaveTime = amplitude / frequency;
qreal samplesPerWave = qCeil(format->sampleRate() * singleWaveTime);
quint32 waveCount = qCeil(msecs / (singleWaveTime * 1000.0));
quint32 sampleSize = static_cast<quint32>(format->sampleSize() / 8.0);
QByteArray data(waveCount * samplesPerWave * sampleSize * format->channelCount(), '\0');
unsigned char* dataPointer = reinterpret_cast<unsigned char*>(data.data());
for (quint32 currentWave = 0; currentWave < waveCount; currentWave++)
{
for (int currentSample = 0; currentSample < samplesPerWave; currentSample++)
{
double nextRadStep = (currentSample / static_cast<double>(samplesPerWave)) * (2 * M_PI);
quint16 sampleValue = static_cast<quint16>((qSin(nextRadStep) > 0 ? 1 : -1) * 32767);
for (int channel = 0; channel < format->channelCount(); channel++)
{
qToLittleEndian(sampleValue, dataPointer);
dataPointer += sampleSize;
}
}
}
soundBuffer->append(data); // <-- STD::Bad_alloc Not enough memory on heap for appending all the frequencies at once in buffer
output->start(outputBuffer);
qDebug() << data.size();
}
}
void MainWindow::on_pushButton_Run_clicked()
{
appendSound();
}
void MainWindow::on_pushButton_Stop_clicked()
{
output->stop();
soundBuffer->clear();
output->reset();
qDebug() << "Playback Stopped";
}
I came up with a solution using QThread and a Interpreter for that thread to wait for more events before continuing the next sequence in the series.
By doing this I don't need to load the full 3~4 hour PCM data into a QBufferArray all at the same time but rather break it up. Run a smaller sequence then wait on the thread to finish then load the next sequence in line and so on until all of them are done playing.
No more std::bad_alloc since the thread only uses around 80mb on the heap at any given time.

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?

Update frame after each iteration

I am currently coding a port-scanner in Qt(C++) for Mac. The process of checking if a certain port is open or not works completely fine. But if the port range which the user wants to check is too big, every port will be checked but the output happens only after this process.
The program actually should check for example port 1 and output the result. After that it should check the next and output and so on...
void MainWindow::checkPort(int portmin, int portmax, string ip) {
int dif = portmax - portmin;
if (dif <= 0)
return;
unsigned int open = 0;
unsigned int closed = 0;
int checked = 0;
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(ip.c_str());
for (int i = portmin; i <= portmax; i++) {
int s = socket(AF_INET, SOCK_STREAM, 0);
addr.sin_port = htons(i);
int con = ::connect(s, reinterpret_cast<sockaddr*>(&addr), sizeof(sockaddr));
if (con == 0){
ui->textEdit->setTextColor(Qt::green);
ui->textEdit->append("Port " + QString::number(i) + " open.");
open++;
}
if (con == -1) {
ui->textEdit->setTextColor(Qt::red);
ui->textEdit->append("Port " + QString::number(i) + " closed.");
closed++;
}
::close(con);
::close(s);
checked++;
}
Have you got any advise how I could have an output after each iteration?
Perhaps something like this:
//...
bool tooManyPorts = dif > 10000; // Set flag to true if port range is too big (for example more than 10 000 ports
//
QString msgs = "";
for (int i = portmin; i <= portmax; i++) {
int s = socket(AF_INET, SOCK_STREAM, 0);
addr.sin_port = htons(i);
if (con == 0){
if (tooManyPorts) {
QString("<font color='green'>Port " + QString::number(i) + " open.</font><br/>");
}
else {
ui->textEdit->setTextColor(Qt::green);
ui->textEdit->append("Port " + QString::number(i) + " open.");
}
open++;
}
if (con == -1) {
if (tooManyPorts) {
msgs += QString("<font color='red'>Port " + QString::number(i) + " closed.</font><br/>");
}
else {
ui->textEdit->setTextColor(Qt::red);
ui->textEdit->append("Port " + QString::number(i) + " closed.");
}
closed++;
}
// ...
}
if(tooManyPorts) {
ui->textEdit->append(msgs); // Add all iteration messages to text edit
}
Note the usage of HTML for the formatting part.
This adds all the output to your field AFTER the loop. To get it to work for each iteration just set msgs = ... and not msgs += ... in the loop and then move the if(tooManyPorts) ... at the end of your for but not outside. Frankly I have difficulty understanding whether you don't want the first version (AFTER the loop) since right now you are adding your output to your text field in each iteration step.
The simplest solution is to run the whole scanning job concurrently using the thread pool. The inter-thread communication is done safely via the signal-slot mechanism:
// https://github.com/KubaO/stackoverflown/tree/master/questions/async-portscan-39469180
#include <QtWidgets>
#include <QtConcurrent>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
class Scanner : public QObject {
Q_OBJECT
bool running = false, stop = false;
int open = 0, closed = 0, total = 0;
void scan() {
running = true;
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
for (int i = 1; i < 65536 && !stop; ++i) {
auto s = socket(AF_INET, SOCK_STREAM, 0);
addr.sin_port = htons(i);
auto con = ::connect(s, reinterpret_cast<sockaddr*>(&addr), sizeof(sockaddr));
emit hasResult(i, con == 0);
con == 0 ? ++open : ++closed;
++total;
::close(s);
}
emit done();
running = false;
}
public:
~Scanner() {
stop = true;
while (running);
}
Q_SIGNAL void hasResult(int port, bool open);
Q_SIGNAL void done();
Q_SLOT void start() {
QtConcurrent::run(this, &Scanner::scan);
}
};
int main(int argc, char ** argv) {
using Q = QObject;
QApplication app{argc, argv};
QWidget ui;
QVBoxLayout layout{&ui};
QTextBrowser log;
QProgressBar bar;
QPushButton scan{"Scan localhost"};
layout.addWidget(&log);
layout.addWidget(&bar);
layout.addWidget(&scan);
bar.setRange(1, 65535);
ui.show();
Scanner scanner;
Q::connect(&scan, &QPushButton::clicked, &scanner, [&]{
scan.setEnabled(false);
scanner.start();
});
Q::connect(&scanner, &Scanner::hasResult, &log, [&](int port, bool isOpen){
bar.setValue(port);
if (!isOpen) return;
auto color = isOpen ? QStringLiteral("green") : QStringLiteral("red");
auto state = isOpen ? QStringLiteral("open") : QStringLiteral("closed");
log.append(QStringLiteral("<font color=\"%1\">Port %2 is %3.</font><br/>").
arg(color).arg(port).arg(state));
});
Q::connect(&scanner, &Scanner::done, &scan, [&]{
bar.reset();
scan.setEnabled(true);
});
return app.exec();
}
#include "main.moc"

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