Related
Data Appending Problem While sending the data through Namedpipe using QT
localserver.h
#ifndef LOCALSERVER_H
#define LOCALSERVER_H
#include "QLocalSocket"
#include "QLocalServer"
class QLocalSocket;
class LocalServer : public QLocalServer
{
Q_OBJECT
public:
explicit LocalServer(QObject *parent = 0);
void send(QString &msj);
QLocalSocket *mSocket;
private slots:
void receive();
private:
};
#endif // LOCALSERVER_H
localserver.cpp
#include "localserver.h"
#include "QLocalSocket"
#include "QTextStream"
#include "QMessageBox"
LocalServer::LocalServer(QObject *parent) : QLocalServer(parent)
{
mSocket = nullptr;
connect(this,&LocalServer::newConnection,[&](){
mSocket = nextPendingConnection();
});
qDebug()<<"Hello World";
}
void LocalServer::send(QString &msj)
{
qDebug()<<"Sending Data";
if(mSocket)
{
QTextStream T(mSocket);
T<<msj.toStdString().c_str();
mSocket->flush();
}
}
void LocalServer::receive()
{
qDebug()<<"Im here";
QDataStream in(mSocket);
in.setVersion(QDataStream::Qt_5_2);
mSocket->readAll();
qDebug()<<"Data Received";
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include "QProcess"
#include "QTimer"
#include "QLocalSocket"
#include "localserver.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
void Connection();
private slots:
void on_start_clicked();
void on_send_clicked();
void on_quit_clicked();
void detect_connection();
void connect_terminate();
void connected();
void send_data();
void receive_data();
QString RandomData();
void on_pbtn_Stop_clicked();
private:
Ui::Widget *ui;
LocalServer *mLocalServer;
QTimer *timer;
QLocalSocket *mSocket;
bool first;
QString data_received;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include "QProcess"
#include "QDebug"
#include "localserver.h"
#include "QMessageBox"
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
Connection();
first = true;
}
void Widget::Connection()
{
mLocalServer = new LocalServer(this);
mLocalServer->listen("Server1");
connect(mLocalServer,SIGNAL(newConnection()),this,SLOT(detect_connection()));
first = true;
timer = new QTimer(this);
timer->setInterval(8);
connect(timer,SIGNAL(timeout()),this,SLOT(send_data()));
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_start_clicked()
{
timer->start();
}
void Widget::on_send_clicked()
{
timer->stop();
}
void Widget::on_quit_clicked()
{
timer->stop();
this->close();
}
void Widget::detect_connection()
{
qDebug()<<"Write Pipe Established";
mSocket = new QLocalSocket(this);
mSocket->connectToServer("Server2");
connect(mSocket,SIGNAL(disconnected()),this,SLOT(connect_terminate()));
connect(mSocket,SIGNAL(connected()),this,SLOT(connected()));
qDebug()<<"Connection Established, Ready to Read";
connect(mSocket,&QLocalSocket::readyRead, [&]() {
QTextStream T(mSocket);
ui->receivedText->addItem(T.readAll());
});
}
void Widget::connect_terminate()
{
qDebug()<<"Read Server Connection Terminated";
QString string = "Stop_Session";
mLocalServer->send(string);
ui->sentText->addItem(string);
}
void Widget::connected()
{
qDebug()<<"Socket Connected to Server";
QString s=" Connection Done ";
mLocalServer->send(s);
}
void Widget::send_data()
{
QString string = RandomData();
mLocalServer->send(string);
ui->sentText->addItem(string);
}
QString Widget::RandomData()
{
srand(time(NULL));
int random_number = rand()%(1920-0 + 1) + 0; // rand() return a number between 0 and RAND_MAX
int random_number1 = rand()%(1080-0 + 1) + 0;
int random_number2 = rand()%(10-0 + 1) + 0;
QString s="Contour,"+QString::number(random_number)+","+QString::number(random_number1)+","+QString::number(random_number2);
// Basically this data in "s" gives the actual contours detected data but am using a random generator to test the Local Server and Socket .
return s;
}
void Widget::receive_data()
{
QString string;
QTextStream T(mSocket);
T.readLine();
if(T.readAll()=="Stop")
{
qDebug()<<"Socket About to Close";
QString string = "Stop";
mLocalServer->send(string);
}
else
{
qDebug()<<"Program can continue"<<T.readAll();
}
}
void Widget::on_pbtn_Stop_clicked()
{
timer->stop();
}
At Receiver SIde Program
Receiver.cpp
void Receiver ::Declaration_Of_Server2()
{
// Declaration Of LocalServer i.e Server2
ServerIS = new LocalServer(this);
ServerIS->listen("Server2");
// Connect To a LocalSocket TO The Above LocalServer i.e Server_IS with Server_VS
connect(ServerIS,SIGNAL(newConnection()),this,SLOT(DetectConnection_WithServer1()));
}
void CoFire_MainPage::DetectConnection_WithServer1()
{
// Declaration of LocalSocket i.e Server_VS
SocketIS = new QLocalSocket(this);
SocketIS->connectToServer("Server1");
connect(SocketIS,SIGNAL(readyRead()),this,SLOT(Receiving_VS_Data()));
}
void CoFire_MainPage::Receiving_VS_Data()
{
// Receiving Vs Data From Socket "Socket_IS"
QTextStream msg(SocketIS);
QString str = msg.readLine();
qDebug()<<" VS DATA : "<<str;
}
By this am trying to send data ( example : Contour,1000,800,1 ) from one program to other program
with a speed of 1 data / 8msec but in real scenario it might even go to 1msec data transfer.
but by this at receving end every now and then incomming data is been appended
(example: Contour,1000,800,1Contour,200,400,1Contour,500,650,1 etc... ).
But actually expentected data is
1st
Contour,1000,800,1
next
Contour,200,400,1
next
Contour,500,650,1
etc....
This way the data is getting appeneded before i capture and process the data at receiver .
By this am loosing the precious information .
how do i solve this issue .
readLine() waits for an end-of-line ("\n" or "\r\n"), but in your case you do not send it, you must change to the following:
QString Widget::RandomData(){
srand(time(NULL));
int random_number = rand()%(1920-0 + 1) + 0; // rand() return a number between 0 and RAND_MAX
int random_number1 = rand()%(1080-0 + 1) + 0;
int random_number2 = rand()%(10-0 + 1) + 0;
return QString("Contour,%1,%2,%3\n")
.arg(random_number)
.arg(random_number1)
.arg(random_number2);
}
On the other hand it is not necessary to use the reference of the QString, and also it is unnecessary to use toStdString().c_str().
void LocalServer::send(const QString &msj)
{
qDebug()<<"Sending Data";
if(mSocket)
{
QTextStream T(mSocket);
T << msj;
mSocket->flush();
}
}
I have a device with microphones that connects to my computer through Ethernet and it cannot be seen by Qt as an audio device, so, I get packets from it and put them to QByteArray. I need to play these packets from stream. Somewhere in the Internet I found a solution to almost the same problem, but there internal microphone was used.
#include <QApplication>
#include <iostream>
#include <cassert>
#include <QCoreApplication>
#include <QAudioInput>
#include <QAudioOutput>
#include <QBuffer>
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
QBuffer rdBuff;
QBuffer wrBuff;
wrBuff.open(QBuffer::WriteOnly);
rdBuff.open(QBuffer::ReadOnly);
QObject::connect(&wrBuff, &QIODevice::bytesWritten, [&wrBuff, &rdBuff](qint64)
{
rdBuff.buffer().remove(0, rdBuff.pos());
// set pointer to the beginning of the unread data
const auto res = rdBuff.seek(0);
assert(res);
// write new data
rdBuff.buffer().append(wrBuff.buffer());
// remove all data that was already written
wrBuff.buffer().clear();
wrBuff.seek(0);
});
const auto decideAudioFormat = [](const QAudioDeviceInfo& devInfo)
{
QAudioFormat format;
format.setSampleRate(8000);
format.setChannelCount(1);
format.setSampleSize(16);
format.setCodec("audio/pcm");
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::SignedInt);
if (devInfo.isFormatSupported(format))
{
return format;
}
else
{
std::cerr << "Raw audio format not supported by backend, cannot play audio.\n";
throw 0;
}
};
QAudioInput audioInput(decideAudioFormat(QAudioDeviceInfo::defaultInputDevice()));
QAudioOutput audioOutput(decideAudioFormat(QAudioDeviceInfo::defaultOutputDevice()));
audioInput.start(&wrBuff);
audioOutput.start(&rdBuff);
return app.exec();
}
It works quite well, but I need to set QByteArray as QAudioInput's source.
Is there any possible solution?
Not sure if i'm directly answering your question. But a possible solution is feed the output audio device manually (push mode) when new data comes.
You can also use a custom (QFile inherited) class to record sound, and when sound come, feeds both the file and output audio device.
Here is a example:
AudioOutput.h:
#ifndef AUDIOOUTPUT_H
#define AUDIOOUTPUT_H
#include <QtCore>
#include <QtMultimedia>
#define MAX_BUFFERED_TIME 10*1000
static inline int timeToSize(int ms, const QAudioFormat &format)
{
return ((format.channelCount() * (format.sampleSize() / 8) * format.sampleRate()) * ms / 1000);
}
class AudioOutput : public QObject
{
Q_OBJECT
public:
explicit AudioOutput(QObject *parent = nullptr);
public slots:
bool start(const QAudioDeviceInfo &devinfo,
const QAudioFormat &format,
int time_to_buffer);
void write(const QByteArray &data);
private slots:
void verifyBuffer();
void preplay();
void play();
private:
bool m_initialized;
QAudioOutput *m_audio_output;
QIODevice *m_device;
QByteArray m_buffer;
bool m_buffer_requested;
bool m_play_called;
int m_size_to_buffer;
int m_time_to_buffer;
int m_max_size_to_buffer;
QAudioFormat m_format;
};
#endif // AUDIOOUTPUT_H
AudioRecorder.h:
#ifndef AUDIORECORDER_H
#define AUDIORECORDER_H
#include <QtCore>
#include <QtMultimedia>
class AudioRecorder : public QFile
{
Q_OBJECT
public:
explicit AudioRecorder(const QString &name, const QAudioFormat &format, QObject *parent = nullptr);
~AudioRecorder();
using QFile::open;
public slots:
bool open();
qint64 write(const QByteArray &data);
void close();
private:
void writeHeader();
bool hasSupportedFormat();
QAudioFormat format;
};
#endif // AUDIORECORDER_H
AudioOutput.cpp:
#include "audiooutput.h"
AudioOutput::AudioOutput(QObject *parent) : QObject(parent)
{
m_initialized = false;
m_audio_output = nullptr;
m_device = nullptr;
m_buffer_requested = true;
m_play_called = false;
m_size_to_buffer = 0;
m_time_to_buffer = 0;
m_max_size_to_buffer = 0;
}
bool AudioOutput::start(const QAudioDeviceInfo &devinfo,
const QAudioFormat &format,
int time_to_buffer)
{
if (!devinfo.isFormatSupported(format))
{
qDebug() << "Format not supported by output device";
return m_initialized;
}
m_format = format;
int internal_buffer_size;
//Adjust internal buffer size
if (format.sampleRate() >= 44100)
internal_buffer_size = (1024 * 10) * format.channelCount();
else if (format.sampleRate() >= 24000)
internal_buffer_size = (1024 * 6) * format.channelCount();
else
internal_buffer_size = (1024 * 4) * format.channelCount();
//Initialize the audio output device
m_audio_output = new QAudioOutput(devinfo, format, this);
//Increase the buffer size to enable higher sample rates
m_audio_output->setBufferSize(internal_buffer_size);
m_time_to_buffer = time_to_buffer;
//Compute the size in bytes to be buffered based on the current format
m_size_to_buffer = timeToSize(m_time_to_buffer, m_format);
//Define a highest size that the buffer are allowed to have in the given time
//This value is used to discard too old buffered data
m_max_size_to_buffer = m_size_to_buffer + timeToSize(MAX_BUFFERED_TIME, m_format);
m_device = m_audio_output->start();
if (!m_device)
{
qDebug() << "Failed to open output audio device";
return m_initialized;
}
//Timer that helps to keep playing data while it's available on the internal buffer
QTimer *timer_play = new QTimer(this);
timer_play->setTimerType(Qt::PreciseTimer);
connect(timer_play, &QTimer::timeout, this, &AudioOutput::preplay);
timer_play->start(10);
//Timer that checks for too old data in the buffer
QTimer *timer_verifier = new QTimer(this);
connect(timer_verifier, &QTimer::timeout, this, &AudioOutput::verifyBuffer);
timer_verifier->start(qMax(m_time_to_buffer, 10));
m_initialized = true;
return m_initialized;
}
void AudioOutput::verifyBuffer()
{
if (m_buffer.size() >= m_max_size_to_buffer)
m_buffer.clear();
}
void AudioOutput::write(const QByteArray &data)
{
m_buffer.append(data);
preplay();
}
void AudioOutput::preplay()
{
if (!m_initialized)
return;
//Verify if exists a pending call to play function
//If not, call the play function async
if (!m_play_called)
{
m_play_called = true;
QMetaObject::invokeMethod(this, "play", Qt::QueuedConnection);
}
}
void AudioOutput::play()
{
//Set that last async call was triggered
m_play_called = false;
if (m_buffer.isEmpty())
{
//If data is empty set that nothing should be played
//until the buffer has at least the minimum buffered size already set
m_buffer_requested = true;
return;
}
else if (m_buffer.size() < m_size_to_buffer)
{
//If buffer doesn't contains enough data,
//check if exists a already flag telling that the buffer comes
//from a empty state and should not play anything until have the minimum data size
if (m_buffer_requested)
return;
}
else
{
//Buffer is ready and data can be played
m_buffer_requested = false;
}
int readlen = m_audio_output->periodSize();
int chunks = m_audio_output->bytesFree() / readlen;
//Play data while it's available in the output device
while (chunks)
{
//Get chunk from the buffer
QByteArray samples = m_buffer.mid(0, readlen);
int len = samples.size();
m_buffer.remove(0, len);
//Write data to the output device
if (len)
m_device->write(samples);
//If chunk is smaller than the output chunk size, exit loop
if (len != readlen)
break;
//Decrease the available number of chunks
chunks--;
}
}
AudioRecorder.cpp:
#include "audiorecorder.h"
AudioRecorder::AudioRecorder(const QString &name, const QAudioFormat &format, QObject *parent) : QFile(name, parent), format(format)
{
}
AudioRecorder::~AudioRecorder()
{
if (!isOpen())
return;
close();
}
bool AudioRecorder::hasSupportedFormat()
{
return (format.sampleSize() == 8
&& format.sampleType() == QAudioFormat::UnSignedInt)
|| (format.sampleSize() > 8
&& format.sampleType() == QAudioFormat::SignedInt
&& format.byteOrder() == QAudioFormat::LittleEndian);
}
bool AudioRecorder::open()
{
if (!hasSupportedFormat())
{
setErrorString("Wav PCM supports only 8-bit unsigned samples "
"or 16-bit (or more) signed samples (in little endian)");
return false;
}
else
{
if (!QFile::open(ReadWrite | Truncate))
return false;
writeHeader();
return true;
}
}
qint64 AudioRecorder::write(const QByteArray &data)
{
return QFile::write(data);
}
void AudioRecorder::writeHeader()
{
QDataStream out(this);
out.setByteOrder(QDataStream::LittleEndian);
// RIFF chunk
out.writeRawData("RIFF", 4);
out << quint32(0); // Placeholder for the RIFF chunk size (filled by close())
out.writeRawData("WAVE", 4);
// Format description chunk
out.writeRawData("fmt ", 4);
out << quint32(16); // "fmt " chunk size (always 16 for PCM)
out << quint16(1); // data format (1 => PCM)
out << quint16(format.channelCount());
out << quint32(format.sampleRate());
out << quint32(format.sampleRate() * format.channelCount()
* format.sampleSize() / 8 ); // bytes per second
out << quint16(format.channelCount() * format.sampleSize() / 8); // Block align
out << quint16(format.sampleSize()); // Significant Bits Per Sample
// Data chunk
out.writeRawData("data", 4);
out << quint32(0); // Placeholder for the data chunk size (filled by close())
Q_ASSERT(pos() == 44); // Must be 44 for WAV PCM
}
void AudioRecorder::close()
{
// Fill the header size placeholders
quint32 fileSize = size();
QDataStream out(this);
// Set the same ByteOrder like in writeHeader()
out.setByteOrder(QDataStream::LittleEndian);
// RIFF chunk size
seek(4);
out << quint32(fileSize - 8);
// data chunk size
seek(40);
out << quint32(fileSize - 44);
QFile::close();
}
main.cpp:
#include <QtCore>
#include "audiooutput.h"
#include "audiorecorder.h"
#include <signal.h>
QByteArray tone_generator()
{
//Tone generator from http://www.cplusplus.com/forum/general/129827/
const unsigned int samplerate = 8000;
const unsigned short channels = 1;
const double pi = M_PI;
const qint16 amplitude = std::numeric_limits<qint16>::max() * 0.5;
const unsigned short n_frequencies = 8;
const unsigned short n_seconds_each = 1;
float frequencies[n_frequencies] = {55.0, 110.0, 220.0, 440.0, 880.0, 1760.0, 3520.0, 7040.0};
const int n_samples = channels * samplerate * n_frequencies * n_seconds_each;
QVector<qint16> data;
data.resize(n_samples);
int index = n_samples / n_frequencies;
for (unsigned short i = 0; i < n_frequencies; ++i)
{
float freq = frequencies[i];
double d = (samplerate / freq);
int c = 0;
for (int j = index * i; j < index * (i + 1); j += 2)
{
double deg = 360.0 / d;
data[j] = data[j + (channels - 1)] = qSin((c++ * deg) * pi / 180.0) * amplitude;
}
}
return QByteArray((char*)data.data(), data.size() * sizeof(qint16));
}
void signalHandler(int signum)
{
qDebug().nospace() << "Interrupt signal (" << signum << ") received.";
qApp->exit();
}
int main(int argc, char *argv[])
{
//Handle console close to ensure destructors being called
#ifdef Q_OS_WIN
signal(SIGBREAK, signalHandler);
#else
signal(SIGHUP, signalHandler);
#endif
signal(SIGINT, signalHandler);
QCoreApplication a(argc, argv);
QAudioFormat format;
format.setSampleRate(8000);
format.setChannelCount(1);
format.setSampleSize(16);
format.setCodec("audio/pcm");
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::SignedInt);
AudioOutput output;
AudioRecorder file("tone.wav", format);
if (!output.start(QAudioDeviceInfo::defaultOutputDevice(), format, 10 * 1000)) //10 seconds of buffer
return a.exec();
if (!file.open())
{
qDebug() << qPrintable(file.errorString());
return a.exec();
}
qDebug() << "Started!";
QByteArray audio_data = tone_generator();
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, [&]{
qDebug() << "Writting" << audio_data.size() << "bytes";
output.write(audio_data);
file.write(audio_data);
});
qDebug() << "Writting" << audio_data.size() << "bytes";
output.write(audio_data);
file.write(audio_data);
timer.start(8000); //8 seconds because we generated 8 seconds of sound
return a.exec();
}
Well. I tried to apply the code (just playback for now) to my situation but there is a problem that it plays data once, if there is no emitting "MorePackets()", or doesn't play at all, if "MorePackets()" occurs.
micserver.h
#ifndef MICSERVER_H
#define MICSERVER_H
//-----------------------------//
#include <QObject>
#include <QDebug>
#include <QTcpServer>
#include <QTcpSocket>
#include <QAudioFormat>
#include <QAudioOutput>
#include <QFile>
#include <QBuffer>
#include <QByteArray>
#include <QDataStream>
#include <iostream>
#include "audiooutput.h"
//-----------------------------//
class MicServer : public QObject {
Q_OBJECT
public:
explicit MicServer(QObject *parent = nullptr);
QTcpSocket* Socket;
private:
QTcpServer* Server;
QAudioFormat format;
AudioOutput output;
QByteArray GetPackets(QTcpSocket* ClientP, int PacketsNumP);
signals:
void MorePackets();
public slots:
void NewConnection();
void Play();
};
//-----------------------------//
#endif
micserver.cpp
#include "micserver.h"
//-----------------------------//
QByteArray MicServer :: GetPackets(QTcpSocket* ClientP, int PacketsNumP) {
QBuffer BufferL;
BufferL.open(QBuffer :: WriteOnly);
QDataStream InputL(&BufferL);
InputL.setVersion(QDataStream::Qt_5_10);
QByteArray TempArray;
for (int i = 0; i < PacketsNumP; i++) {
ClientP -> waitForReadyRead(3000);
InputL << ClientP -> readAll();
}
for (int i = 0; i < PacketsNumP; i++) {
TempArray.push_back(BufferL.buffer().mid(76 + i * 1172, 256));
}
BufferL.close();
return TempArray;
}
//-----------------------------//
MicServer :: MicServer(QObject *parent) : QObject(parent) {
Server = new QTcpServer;
if (!Server -> listen(QHostAddress :: Any, 49112)) {
qDebug() << "Failed to launch server!";
} else {
qDebug() << "Server launched!";
}
connect(Server, SIGNAL(newConnection()), this, SLOT(NewConnection()));
connect(Server, SIGNAL(newConnection()), this, SLOT(Play()));
connect(this, SIGNAL(MorePackets()), this, SLOT(Play()));
format.setSampleRate(16000);
format.setChannelCount(1);
format.setSampleSize(16);
format.setCodec("audio/pcm");
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::SignedInt);
output.start(QAudioDeviceInfo::defaultOutputDevice(), format);
}
void MicServer :: NewConnection() {
Socket = Server -> nextPendingConnection();
qDebug() << "New connection!";
qDebug() << Socket -> localAddress().toString();
}
void MicServer :: Play() {
QByteArray audio_data = GetPackets(Socket, 250);
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, [&]{
qDebug() << "Writting" << audio_data.size() << "bytes";
output.write(audio_data);
});
qDebug() << "Writting" << audio_data.size() << "bytes";
output.write(audio_data);
timer.start(2000);
emit(MorePackets());
}
I'm here to seek help on a strange behavior In am having w/ my project. So here goes !
Issue
I have a receiving class called DataReceiver, using a QTcpServer and a QTcpSocket, and I have the DataSender class feeding it via a QTcpSocket. I send the data periodically, and trigger the "send" slot w/ a QTimer.
BUT, after a few iterations, the feed stalls, and is not periodic anymore. I can't really understand what is happening.
I have confirmed this issue w/ terminal printing on the Receiver side.
The code
datasender.cpp
// Con/Destructors
DataSender::DataSender(QObject *parent) :
QObject(parent),
mTcpSocket(new QTcpSocket(this)),
mDestinationAddress("127.0.0.1"),
mDestinationPort(51470),
mTimer(new QTimer(this))
{
connectToHost();
connect(mTimer, SIGNAL(timeout(void)), this, SLOT(sendPeriodicData()));
mTimer->start(1000);
}
DataSender::~DataSender(){
mTcpSocket->disconnectFromHost();
mTcpSocket->waitForDisconnected();
delete mTcpSocket;
}
// Network Management
bool DataSender::connectToHost(void){
connect(mTcpSocket, SIGNAL(connected()), this, SLOT(onConnect()));
connect(mTcpSocket, SIGNAL(disconnected()), this, SLOT(onDisconnect()));
connect(mTcpSocket, SIGNAL(bytesWritten(qint64)), this, SLOT(onBytesWritten(qint64)));
qDebug() << "connecting...";
mTcpSocket->setSocketOption(QAbstractSocket::KeepAliveOption, true);
mTcpSocket->setSocketOption(QAbstractSocket::WriteOnly);
mTcpSocket->connectToHost(getDestinationAddress(), getDestinationPort());
if(!mTcpSocket->waitForConnected(1000))
{
qDebug() << "Error: " << mTcpSocket->errorString();
return false;
}
// Setting meteo data to send
mMeteoData.messageID = METEO_MESSAGE;
mMeteoData.temperature = 5.5;
mMeteoData.pressure = 10.2;
mMeteoData.humidity = 45.5;
// Setting positiondata to send
mPositionData.messageID = POSITION_MESSAGE;
mPositionData.north = 120.3;
mPositionData.pitch = 1.5;
mPositionData.roll = 2.5;
mPositionData.yaw = 3.5;
mPositionData.a_x = 30.5;
mPositionData.a_y = 40.5;
mPositionData.a_z = 50.5;
return true;
}
void DataSender::sendData(void) const{
QByteArray lData("Hello, this is DataSender ! Do you copy ? I repeat, do you copy ?");
if(mTcpSocket->state() == QAbstractSocket::ConnectedState)
{
mTcpSocket->write(lData);
mTcpSocket->waitForBytesWritten();
}
}
void DataSender::sendData(const QByteArray &pData) const{
//QByteArray lData("Hello, this is DataSender ! Do you copy ? I repeat, do you copy ?");
if(mTcpSocket->state() == QAbstractSocket::ConnectedState)
{
mTcpSocket->write(pData);
mTcpSocket->waitForBytesWritten();
mTcpSocket->flush();
//usleep(1000);
}
}
// Getters
QString DataSender::getDestinationAddress(void) const{
return mDestinationAddress;
}
unsigned int DataSender::getDestinationPort(void) const{
return mDestinationPort;
}
// Setters
void DataSender::setDestinationAddress(const QString &pDestinationAddress){
mDestinationAddress = pDestinationAddress;
}
void DataSender::setDestinationPort(const unsigned int &pDestinationPort){
mDestinationPort = pDestinationPort;
}
// Public Slots
void DataSender::onConnect(){
qDebug() << "connected...";
}
void DataSender::onDisconnect(){
qDebug() << "disconnected...";
}
void DataSender::onBytesWritten(qint64 bytes){
qDebug() << bytes << " bytes written...";
}
void DataSender::sendPeriodicData(void){
mTcpSocket->
// Changing data for testing
mPositionData.north += 10;
mPositionData.north = std::fmod(mPositionData.north, 360);
mMeteoData.temperature += 10;
mMeteoData.temperature = std::fmod(mMeteoData.temperature, 500);
// Declaring QByteArrays
QByteArray lMeteoByteArray;
QByteArray lPositionByteArray;
// Serializing
lMeteoByteArray = serializeMeteoData(mMeteoData);
lPositionByteArray = serializePositionData(mPositionData);
// Sending
sendData(lMeteoByteArray);
sendData(lPositionByteArray);
}
datareceiver.cpp
// Con/Destructors
DataReceiver::DataReceiver(QObject *parent) :
QObject(parent),
mTcpServer(new QTcpServer(this)),
mSourceAddress("127.0.0.1"),
mSourcePort(51470)
{
initData();
connect(mTcpServer, SIGNAL(newConnection()), this, SLOT(onNewConnection()));
if(!mTcpServer->listen(QHostAddress(getSourceAddress()), getSourcePort()))
qDebug() << "<DataReceiver> Server could not start. ";
else
qDebug() << "<DataReceiver> Server started !";
}
DataReceiver::DataReceiver(const QString &pSourceAddress,
const unsigned int &pSourcePort,
QObject *parent) :
QObject(parent),
mTcpServer(new QTcpServer(this)),
mSourceAddress(pSourceAddress),
mSourcePort(pSourcePort)
{
initData();
connect(mTcpServer, SIGNAL(newConnection()), this, SLOT(onNewConnection()));
if(!mTcpServer->listen(QHostAddress(getSourceAddress()), getSourcePort()))
qDebug() << "<DataReceiver> Server could not start. ";
else
qDebug() << "<DataReceiver> Server started !";
}
DataReceiver::~DataReceiver(){
if(mTcpSocket != nullptr) delete mTcpSocket;
delete mTcpServer;
if(mTcpSocket != nullptr)
delete mTcpSocket;
}
// Getters
QTcpServer *DataReceiver::getTcpServer(void) const{
return mTcpServer;
}
QString DataReceiver::getSourceAddress(void) const{
return mSourceAddress;
}
unsigned int DataReceiver::getSourcePort(void) const{
return mSourcePort;
}
positionData_t DataReceiver::getPositionData(void) const{
return mPositionData;
}
meteoData_t DataReceiver::getMeteoData(void) const{
return mMeteoData;
}
// Setters
void DataReceiver::setSourceAddress(const QString &pSourceAddress){
mSourceAddress = pSourceAddress;
}
void DataReceiver::setSourcePort(const unsigned int &pSourcePort){
mSourcePort = pSourcePort;
}
void DataReceiver::setPositionData(const positionData_t &pPositionData){
mPositionData = pPositionData;
}
void DataReceiver::setMeteoData(const meteoData_t &pMeteoData){
mMeteoData = pMeteoData;
}
// Data Management
void DataReceiver::initPositionData(void){
mPositionData.messageID = METEO_MESSAGE;
mPositionData.pitch = .0;
mPositionData.roll = .0;
mPositionData.yaw = .0;
mPositionData.a_x = .0;
mPositionData.a_y = .0;
mPositionData.a_z = .0;
}
void DataReceiver::initMeteoData(void){
mMeteoData.messageID = POSITION_MESSAGE;
mMeteoData.temperature = .0;
mMeteoData.humidity = .0;
mMeteoData.pressure = .0;
}
void DataReceiver::initData(void){
initPositionData();
initMeteoData();
}
void DataReceiver::reinitData(void){
initData();
}
// Public Slots
void DataReceiver::onConnect(){
qDebug() << "QTcpSocket connected...";
}
void DataReceiver::onDisconnect(){
qDebug() << "QTcpSocket disconnected...";
disconnect(mTcpSocket, SIGNAL(readyRead()), this, SLOT(onDataReceived()));
disconnect(mTcpSocket, SIGNAL(disconnected()), this, SLOT(onDisconnect()));
}
void DataReceiver::onBytesWritten(qint64 bytes){
qDebug() << bytes << " bytes written to QTcpSocket...";
}
void DataReceiver::onDataReceived(){
// Not yet implemented, code is for testing
qDebug() << "onDataReceived called !";
QByteArray lReceivedData;
while(mTcpSocket->bytesAvailable()){
lReceivedData = mTcpSocket->read(mTcpSocket->bytesAvailable());
decodeData(lReceivedData);
qDebug() << lReceivedData << "\n";
}
}
void DataReceiver::onNewConnection(){
qDebug() << "onNewConnection called !";
mTcpSocket = mTcpServer->nextPendingConnection();
mTcpSocket->setSocketOption(QAbstractSocket::ReadOnly, true);
connect(mTcpSocket, SIGNAL(readyRead()), this, SLOT(onDataReceived()));
connect(mTcpSocket, SIGNAL(disconnected()), this, SLOT(onDisconnect()));
}
void DataReceiver::onDataChanged(void){
qDebug() << "onDataChanged called !";
qDebug() << "\nPrinting mMeteoData : ";
qDebug() << "mMeteoData.messageID" << mMeteoData.messageID;
qDebug() << "mMeteoData.temperature" << mMeteoData.temperature;
qDebug() << "mMeteoData.humidity" << mMeteoData.humidity;
qDebug() << "mMeteoData.pressure" << mMeteoData.pressure;
qDebug() << "\nPrinting mPositionData";
qDebug() << "mPositionData.messageID" << mPositionData.messageID;
qDebug() << "mPositionData.north" << mPositionData.north;
qDebug() << "mPositionData.pitch" << mPositionData.pitch;
qDebug() << "mPositionData.roll" << mPositionData.roll;
qDebug() << "mPositionData.yaw" << mPositionData.yaw;
qDebug() << "mPositionData.a_x" << mPositionData.a_x;
qDebug() << "mPositionData.a_y" << mPositionData.a_y;
qDebug() << "mPositionData.a_z" << mPositionData.a_z;
}
// Private Methods
void DataReceiver::decodeData(const QByteArray &pMessage){
// Not yet implemented
quint8 tempMessageID = fetchMessageID(pMessage);
switch(tempMessageID){
case UNKNOWN_MESSAGE:
break;
case METEO_MESSAGE:
mMeteoData = deserializeMeteoData(pMessage);
emit dataChanged();
break;
case POSITION_MESSAGE:
mPositionData = deserializePositionData(pMessage);
emit dataChanged();
break;
}
return;
}
More information
I send two types of data, both coming from structs. On the receiver side, I decode the data, identify it, and set corresponding attributes.
The receiver class is used in a QT GUI App. It is not particularly threaded. I do not really know if it is necessary, as at the beginning the data is received periodically as intended.
Conclusion
I would really like to find a solution for this... I tried a few things to no avail, like changing how I read the socket or using the QTcpSocket::flush() method (not sure it does what I thought it did)
Thanks a lot,
Clovel
Bonus
I would also like to be able to open de Receiver end and have the sender automatically detect it and start sending the data to the Receiver. I need to dig a little more into this, but if you have a tip, it would be welcome !
While the #Kuba Ober comment seems to solve the lock of your sender, i will also advice about TCP, because one send operation can result in multiples readyRead SIGNALs and also multiples send operations can result in a single readyRead SIGNAL, so my advice is to write a small protocol to avoid problems with corrupted data!
I wrote a protocol here and it was well accepted, now i'm improving it!
The following example is generic, so you have to adapt it to fit your needs, but i think you can do it without much hassle :), it has been tested with Qt 5.5.1 MinGW in Windows 10.
To reach your bonus i used a UDP socket to send a broadcast message and try to find peers in range.
You can see the full project here!
common.h:
#ifndef COMMON_H
#define COMMON_H
#include <QtCore>
#include <QtNetwork>
//Helper macro to set a QObject pointer to nullptr when it's get destroyed
#define SETTONULLPTR(obj) QObject::connect(obj, &QObject::destroyed, [=]{obj = nullptr;})
//Define a max size for each send data operation
#define MAX_NETWORK_CHUNK_SIZE 10*1024*1024
//Create differents types for incomming data, valid for both client and server
namespace Type
{
enum
{
DataType1,
DataType2
};
}
//Convert some data of type T to QByteArray, by default in big endian order
template <typename T>
static inline QByteArray getBytes(T input)
{
QByteArray tmp;
QDataStream data(&tmp, QIODevice::WriteOnly);
data << input;
return tmp;
}
//Convert some QByteArray to data of type T
template <typename T>
static inline T getValue(QByteArray bytes)
{
T tmp;
QDataStream data(&bytes, QIODevice::ReadOnly);
data >> tmp;
return tmp;
}
//Struct that holds data and information about the peer
typedef struct PeerData {
QByteArray data;
QHostAddress host;
qintptr descriptor;
} PeerData;
#endif // COMMON_H
client.h:
#ifndef CLIENT_H
#define CLIENT_H
#include <QtCore>
#include <QtNetwork>
#include "common.h"
class Client : public QObject
{
Q_OBJECT
public:
explicit Client(QObject *parent = nullptr);
~Client();
signals:
void peerFound(QHostAddress);
void searchPeersFinished();
void connected(PeerData);
void disconnected(PeerData);
void readyRead(PeerData);
void error(QString);
public slots:
void abort();
void connectToHost(const QString &host, quint16 port);
void searchPeers(quint16 port);
void stop();
int write(const QByteArray &data);
private slots:
void UDPReadyRead();
void UDPWrite();
void searchPeersEnd();
void timeout();
void connectedPrivate();
void disconnectedPrivate();
void errorPrivate(QAbstractSocket::SocketError e);
void readyReadPrivate();
private:
QTcpSocket *m_socket;
QUdpSocket *m_udp_socket;
quint16 m_udp_port;
QByteArray m_buffer;
qint32 m_size;
QTimer *m_timer;
};
#endif // CLIENT_H
client.cpp:
#include "client.h"
Client::Client(QObject *parent) : QObject(parent)
{
qRegisterMetaType<PeerData>("PeerData");
qRegisterMetaType<QHostAddress>("QHostAddress");
m_socket = nullptr;
m_size = 0;
m_udp_socket = nullptr;
m_timer = new QTimer(this);
connect(m_timer, &QTimer::timeout, this, &Client::timeout);
m_timer->setSingleShot(true);
}
Client::~Client()
{
disconnectedPrivate();
}
//Disconnects the socket
void Client::abort()
{
if (m_socket)
m_socket->abort();
}
//Start connection to server
void Client::connectToHost(const QString &host, quint16 port)
{
if (m_socket)
return;
m_socket = new QTcpSocket(this);
SETTONULLPTR(m_socket);
connect(m_socket, &QTcpSocket::readyRead, this, &Client::readyReadPrivate);
connect(m_socket, &QTcpSocket::connected, this, &Client::connectedPrivate);
connect(m_socket, &QTcpSocket::disconnected, this, &Client::disconnectedPrivate);
connect(m_socket, static_cast<void(QTcpSocket::*)(QTcpSocket::SocketError)>(&QTcpSocket::error), this, &Client::errorPrivate);
m_timer->start(10 * 1000);
m_socket->connectToHost(host, port);
}
//Perform udp broadcast to search peers
void Client::searchPeers(quint16 port)
{
if (m_udp_socket)
return;
m_udp_socket = new QUdpSocket(this);
SETTONULLPTR(m_udp_socket);
connect(m_udp_socket, &QUdpSocket::readyRead, this, &Client::UDPReadyRead);
m_udp_port = port;
QTimer::singleShot(50, this, &Client::UDPWrite);
}
//Ready read specific for udp socket
void Client::UDPReadyRead()
{
while (m_udp_socket->hasPendingDatagrams())
{
QByteArray data;
data.resize(m_udp_socket->pendingDatagramSize());
QHostAddress peer_address;
quint16 peer_port;
m_udp_socket->readDatagram(data.data(), data.size(), &peer_address, &peer_port);
//Test the header used in this udp broadcast, you can freely change this value,
//but do for both client and server
if (QLatin1String(data) == QLatin1Literal("TEST"))
emit peerFound(peer_address);
}
}
//Send the udp broadcast message to all the network interfaces
void Client::UDPWrite()
{
QList<QHostAddress> broadcast;
foreach (QHostAddress address, QNetworkInterface::allAddresses())
{
if (address.protocol() == QAbstractSocket::IPv4Protocol)
{
address.setAddress(address.toIPv4Address());
QStringList list = address.toString().split(".");
list.replace(3, "255");
QString currentbroadcast = list.join(".");
QHostAddress address = QHostAddress(QHostAddress(currentbroadcast).toIPv4Address());
broadcast.append(address);
}
}
QByteArray datagram = QString("TEST").toLatin1();
foreach (const QHostAddress &address, broadcast)
m_udp_socket->writeDatagram(datagram, address, m_udp_port);
//Wait 0.5 seconds for an answer
QTimer::singleShot(500, this, &Client::searchPeersEnd);
}
//Stop the udp socket
void Client::searchPeersEnd()
{
m_udp_socket->deleteLater();
emit searchPeersFinished();
}
void Client::timeout()
{
emit error("Operation timed out");
stop();
}
//Handle connected state
void Client::connectedPrivate()
{
QHostAddress host = m_socket->peerAddress();
qintptr descriptor = m_socket->socketDescriptor();
PeerData pd;
pd.host = host;
pd.descriptor = descriptor;
emit connected(pd);
m_timer->stop();
}
//Handle disconnected state
void Client::disconnectedPrivate()
{
if (!m_socket)
return;
QHostAddress host = m_socket->peerAddress();
qintptr descriptor = m_socket->socketDescriptor();
stop();
PeerData pd;
pd.host = host;
pd.descriptor = descriptor;
emit disconnected(pd);
}
//Handle error
void Client::errorPrivate(QAbstractSocket::SocketError e)
{
if (e != QAbstractSocket::RemoteHostClosedError)
{
QString err = m_socket->errorString();
emit error(err);
}
stop();
}
//Stop the tcp socket
void Client::stop()
{
m_timer->stop();
m_size = 0;
m_buffer.clear();
if (m_socket)
{
m_socket->abort();
m_socket->deleteLater();
}
}
//Write data to the server
int Client::write(const QByteArray &data)
{
if (!m_socket)
return 0;
m_socket->write(getBytes<qint32>(data.size()));
m_socket->write(data);
return 1;
}
//Receive message from server
void Client::readyReadPrivate()
{
if (!m_socket)
return;
while (m_socket->bytesAvailable() > 0)
{
m_buffer.append(m_socket->readAll());
while ((m_size == 0 && m_buffer.size() >= 4) || (m_size > 0 && m_buffer.size() >= m_size))
{
if (m_size == 0 && m_buffer.size() >= 4)
{
m_size = getValue<qint32>(m_buffer.mid(0, 4));
m_buffer.remove(0, 4);
if (m_size < 0 || m_size > MAX_NETWORK_CHUNK_SIZE)
{
m_socket->abort();
return;
}
}
if (m_size > 0 && m_buffer.size() >= m_size)
{
QByteArray data = m_buffer.mid(0, m_size);
m_buffer.remove(0, m_size);
m_size = 0;
QHostAddress host = m_socket->peerAddress();
qintptr descriptor = m_socket->socketDescriptor();
PeerData pd;
pd.data = data;
pd.host = host;
pd.descriptor = descriptor;
emit readyRead(pd);
}
}
}
}
server.h:
#ifndef SERVER_H
#define SERVER_H
#include <QtCore>
#include <QtNetwork>
#include "common.h"
class Server : public QObject
{
Q_OBJECT
public:
explicit Server(QObject *parent = nullptr);
~Server();
signals:
void connected(PeerData);
void disconnected(PeerData);
void listening(quint16);
void readyRead(PeerData);
void error(QString);
public slots:
void abort(qintptr descriptor);
void listen(quint16 port);
void stop();
void writeToHost(const QByteArray &data, qintptr descriptor);
int writeToAll(const QByteArray &data);
private slots:
void newConnectionPrivate();
void UDPListen(quint16 port);
void UDPReadyRead();
void readyReadPrivate();
void disconnectedPrivate();
void removeSocket(QTcpSocket *socket);
private:
QTcpServer *m_server;
QUdpSocket *m_udp_server;
QList<QTcpSocket*> m_socket_list;
QHash<qintptr, QTcpSocket*> m_socket_hash;
QHash<QTcpSocket*, qintptr> m_descriptor_hash;
QHash<QTcpSocket*, QByteArray> m_buffer_hash;
QHash<QTcpSocket*, qint32> m_size_hash;
};
#endif // SERVER_H
server.cpp:
#include "server.h"
Server::Server(QObject *parent) : QObject(parent)
{
qRegisterMetaType<PeerData>("PeerData");
qRegisterMetaType<QHostAddress>("QHostAddress");
m_server = nullptr;
m_udp_server = nullptr;
}
Server::~Server()
{
stop();
}
//Disconnect the socket specified by descriptor
void Server::abort(qintptr descriptor)
{
QTcpSocket *socket = m_socket_hash.value(descriptor);
socket->abort();
}
//Try to start in listening state
void Server::listen(quint16 port)
{
if (m_server)
return;
m_server = new QTcpServer(this);
SETTONULLPTR(m_server);
connect(m_server, &QTcpServer::newConnection, this, &Server::newConnectionPrivate);
if (port < 1)
{
emit error("Invalid port value");
stop();
return;
}
bool is_listening = m_server->listen(QHostAddress::AnyIPv4, port);
if (!is_listening)
{
emit error(m_server->errorString());
stop();
return;
}
UDPListen(port);
emit listening(m_server->serverPort());
}
//Start the udp server, used to perform netowork search
void Server::UDPListen(quint16 port)
{
if (m_udp_server)
return;
m_udp_server = new QUdpSocket(this);
SETTONULLPTR(m_udp_server);
connect(m_udp_server, &QUdpSocket::readyRead, this, &Server::UDPReadyRead);
m_udp_server->bind(port);
}
//ReadyRead specific for udp
void Server::UDPReadyRead()
{
while (m_udp_server->hasPendingDatagrams())
{
QByteArray data;
QHostAddress address;
quint16 port;
data.resize(m_udp_server->pendingDatagramSize());
m_udp_server->readDatagram(data.data(), data.size(), &address, &port);
//Test the header used in this udp broadcast, you can freely change this value,
//but do for both client and server
if (QLatin1String(data) == QLatin1Literal("TEST"))
{
m_udp_server->writeDatagram(data, address, port);
}
}
}
//Stop both tcp and udp servers and also
//removes peers that may be connected
void Server::stop()
{
while (!m_socket_list.isEmpty())
removeSocket(m_socket_list.first());
if (m_server)
{
m_server->close();
m_server->deleteLater();
}
if (m_udp_server)
{
m_udp_server->deleteLater();
}
}
//Handle new connection
void Server::newConnectionPrivate()
{
while (m_server->hasPendingConnections())
{
QTcpSocket *socket = m_server->nextPendingConnection();
QHostAddress host = socket->peerAddress();
qintptr descriptor = socket->socketDescriptor();
QByteArray m_buffer;
qint32 size = 0;
m_descriptor_hash.insert(socket, descriptor);
m_socket_hash.insert(descriptor, socket);
m_buffer_hash.insert(socket, m_buffer);
m_size_hash.insert(socket, size);
m_socket_list.append(socket);
connect(socket, &QTcpSocket::disconnected, this, &Server::disconnectedPrivate);
connect(socket, &QTcpSocket::readyRead, this, &Server::readyReadPrivate);
PeerData pd;
pd.host = host;
pd.descriptor = descriptor;
emit connected(pd);
}
}
//Write to specific socket if more than one is connected
void Server::writeToHost(const QByteArray &data, qintptr descriptor)
{
if (!m_socket_hash.contains(descriptor))
return;
QTcpSocket *socket = m_socket_hash.value(descriptor);
socket->write(getBytes<qint32>(data.size()));
socket->write(data);
}
//Write to all sockets
int Server::writeToAll(const QByteArray &data)
{
foreach (QTcpSocket *socket, m_socket_list)
{
socket->write(getBytes<qint32>(data.size()));
socket->write(data);
}
return m_socket_list.size();
}
//ReadyRead function shared by all sockets connected
void Server::readyReadPrivate()
{
QTcpSocket *socket = static_cast<QTcpSocket*>(sender());
QByteArray *m_buffer = &m_buffer_hash[socket];
qint32 *size = &m_size_hash[socket];
Q_UNUSED(size)
#define m_size *size
while (socket->bytesAvailable() > 0)
{
m_buffer->append(socket->readAll());
while ((m_size == 0 && m_buffer->size() >= 4) || (m_size > 0 && m_buffer->size() >= m_size))
{
if (m_size == 0 && m_buffer->size() >= 4)
{
m_size = getValue<qint32>(m_buffer->mid(0, 4));
m_buffer->remove(0, 4);
if (m_size < 0 || m_size > MAX_NETWORK_CHUNK_SIZE)
{
socket->abort();
return;
}
}
if (m_size > 0 && m_buffer->size() >= m_size)
{
QByteArray data = m_buffer->mid(0, m_size);
m_buffer->remove(0, m_size);
m_size = 0;
QHostAddress host = socket->peerAddress();
qintptr descriptor = socket->socketDescriptor();
PeerData pd;
pd.data = data;
pd.host = host;
pd.descriptor = descriptor;
emit readyRead(pd);
}
}
}
}
//Handle socket disconnection
void Server::disconnectedPrivate()
{
QTcpSocket *socket = static_cast<QTcpSocket*>(sender());
QHostAddress host = socket->peerAddress();
qintptr descriptor = m_descriptor_hash.value(socket);
removeSocket(socket);
PeerData pd;
pd.host = host;
pd.descriptor = descriptor;
emit disconnected(pd);
}
//Handle socket removal
void Server::removeSocket(QTcpSocket *socket)
{
qintptr descriptor = m_descriptor_hash.value(socket);
m_socket_hash.remove(descriptor);
m_descriptor_hash.remove(socket);
m_buffer_hash.remove(socket);
m_size_hash.remove(socket);
m_socket_list.removeAll(socket);
socket->abort();
socket->deleteLater();
}
Client/worker.h
#ifndef WORKER_H
#define WORKER_H
#include <QtCore>
#include "client.h"
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = nullptr);
public slots:
void start(quint16 port);
private:
Client m_client;
quint16 m_port;
QTimer m_timer;
double m_double_1;
double m_double_2;
QMetaObject::Connection m_connection;
};
#endif // WORKER_H
Client/worker.cpp
#include "worker.h"
Worker::Worker(QObject *parent) : QObject(parent)
{
m_port = 0;
m_double_1 = 0;
m_double_2 = 0;
m_timer.setInterval(1000);
}
void Worker::start(quint16 port)
{
m_port = port;
connect(&m_client, &Client::searchPeersFinished, []{
qDebug() << "Search peers finished!";
});
m_connection = connect(&m_client, &Client::peerFound, [&](const QHostAddress &peer_address){
disconnect(m_connection); //Disconnect signal, only first peer found will be handled!
m_client.connectToHost(QHostAddress(peer_address.toIPv4Address()).toString(), m_port);
});
connect(&m_client, &Client::error, [](const QString &error){
qDebug() << "Error:" << qPrintable(error);
});
connect(&m_client, &Client::connected, [&](const PeerData &pd){
qDebug() << "Connected to:" << qPrintable(pd.host.toString());
m_timer.start();
});
connect(&m_client, &Client::disconnected, [](const PeerData &pd){
qDebug() << "Disconnected from:" << qPrintable(pd.host.toString());
});
connect(&m_client, &Client::readyRead, [](const PeerData &pd){
qDebug() << "Data from" << qPrintable(pd.host.toString())
<< qPrintable(QString::asprintf("%.2f", getValue<double>(pd.data)));
});
connect(&m_timer, &QTimer::timeout, [&]{
m_double_1 += 0.5; //Just an example of data
QByteArray data1;
data1.append(getBytes<quint8>(Type::DataType1)); //Data 1 has Type 1, added as header
data1.append(getBytes<double>(m_double_1)); //The data itself
m_client.write(data1); //Write the data1 to the server
m_double_2 += 1.0; //Just an example of data
QByteArray data2;
data2.append(getBytes<quint8>(Type::DataType2)); //Data 2 has Type 2, added as header
data2.append(getBytes<double>(m_double_2)); //The data itself
m_client.write(data2); //Write the data2 to the server
});
qDebug() << "Searching...";
m_client.searchPeers(m_port); //Search for peers in range, if found, handle the first and connect to it!
}
Client/main.cpp
#include <QtCore>
#include "worker.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Worker worker;
worker.start(1024);
return a.exec();
}
Server/worker.h
#ifndef WORKER_H
#define WORKER_H
#include <QtCore>
#include "server.h"
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = nullptr);
public slots:
void start(quint16 port);
private:
Server m_server;
quint16 m_port;
};
#endif // WORKER_H
Server/worker.cpp
#include "worker.h"
Worker::Worker(QObject *parent) : QObject(parent)
{
m_port = 0;
}
void Worker::start(quint16 port)
{
m_port = port;
connect(&m_server, &Server::error, [](const QString &error){
if (!error.isEmpty())
qDebug() << "Error:" << qPrintable(error);
});
connect(&m_server, &Server::listening, [](quint16 port){
qDebug() << "Listening on port:" << port;
});
connect(&m_server, &Server::connected, [](const PeerData &pd){
qDebug() << "Connected to:" << qPrintable(pd.host.toString());
});
connect(&m_server, &Server::disconnected, [](const PeerData &pd){
qDebug() << "Disconnected from:" << qPrintable(pd.host.toString());
});
connect(&m_server, &Server::readyRead, [&](const PeerData &pd){
QByteArray data = pd.data;
if (data.isEmpty())
return;
quint8 header = getValue<quint8>(data.mid(0, 1)); //Read the 1 byte header
data.remove(0, 1); //Remove the header from data
switch (header)
{
case Type::DataType1:
{
qDebug() << "Data from" << qPrintable(pd.host.toString())
<< "DataType1" << qPrintable(QString::asprintf("%.2f", getValue<double>(data)));
break;
}
case Type::DataType2:
{
qDebug() << "Data from" << qPrintable(pd.host.toString())
<< "DataType2" << qPrintable(QString::asprintf("%.2f", getValue<double>(data)));
break;
}
default:
break;
}
m_server.writeToHost(data, pd.descriptor);
});
m_server.listen(m_port);
}
Server/main.cpp
#include <QtCore>
#include "worker.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Worker worker;
worker.start(1024);
return a.exec();
}
I try to get serial communication working, but struggle at waiting for the response. The logic should be to send data, wait for a response, get the response and then repeat.
To speed up my code and preventing it from blocking other independent parts of the code I run the serial communication in a separate thread.
The problem is in the send function, where I always get a "Timeout" message instead of an "Success" message.
serial.h
#ifndef SERIAL_H
#define SERIAL_H
#include <QThread>
#include <QDebug>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QStringList>
class Serial : public QThread
{
Q_OBJECT
public:
Serial();
bool send(QString text);
bool connectSerial();
void disconnectSerial();
signals:
void dataReceived(QByteArray receivedData);
protected:
void run();
private slots:
void onGetData();
private:
void readAngles();
QSerialPort qsp;
QSerialPortInfo qspi;
QByteArray receivedData;
};
#endif // SERIAL_H
serial.cpp
#include "serial.h"
Serial::Serial()
{
connect(&qsp, SIGNAL(readyRead()), this, SLOT(onGetData()));
}
bool Serial::send(QString text)
{
if (qsp.isWritable()) {
QByteArray buffer = text.toLatin1();
if (buffer.size() != qsp.write(buffer))
qDebug() << "Send does not work";
qsp.flush();
if(!qsp.waitForReadyRead(2500)) {
qDebug() << "Timeout";
} else {
qDebug() << "Success";
}
return true;
} else {
return false;
}
}
void Serial::onGetData()
{
qDebug() << "onGetData called";
qDebug() << qsp.readAll();
}
bool Serial::connectSerial(int port)
{
qDebug() << qspi.availablePorts().count();
for(int i=0; i<qspi.availablePorts().count(); i++) {
qDebug() << qspi.availablePorts().at(i).description();
}
qsp.setPort(qspi.availablePorts().at(port));
qsp.setBaudRate(QSerialPort::Baud9600);
qsp.setDataBits(QSerialPort::Data7);
qsp.setFlowControl(QSerialPort::NoFlowControl);
qsp.setParity(QSerialPort::NoParity);
qsp.setStopBits(QSerialPort::OneStop);
return qsp.open(QIODevice::ReadWrite);
}
/**
* #brief Disconnecting from serial port.
*/
void Serial::disconnectSerial()
{
if (qsp.isOpen()) {
qsp.close();
}
}
void Serial::readAngles()
{
send("012345");
}
In my program I have a child process that interacts with a serial port specified to it when the parent process demands it. For example the parent process commands the child process to read a number of bytes with a certain time out from the opened port by sending this command: read 100 1000. The child process launches and opens the port successfully and I can see the message port openned successfully! but from there onwards it won't read the parent commands.
Here's the child source code:
SerialPortHandler.h
#ifndef SERIALPORTHANDLER_H
#define SERIALPORTHANDLER_H
#include <QObject>
#include <QSocketNotifier>
#include <QTextStream>
#include <QSerialPort>
#include <QFile>
#include <QTimer>
#include <QDebug>
#include <QtCore>
enum CommandType { READ, WRITE, BAD, UNKNOWN };
class SerialPortHandler : public QObject
{
Q_OBJECT
public:
explicit SerialPortHandler(QString portname, QString baudrate, QObject *parent = 0);
signals:
public slots:
void execmd();
bool open(const QString& portname, const QString& baudrate);
qint64 read(char * buff, const qint64 size, const qint32 timeout);
QString convertToCaseInsensitiveRegExp(QString str);
CommandType analyze(const QString& line);
qint64 getNum(const QString &line, int index);
void reply(char *buff);
private:
QSocketNotifier *innotif;
QSerialPort *sp;
QTimer *timer;
};
#endif // SERIALPORTHANDLER_H
SerialPortHandler.cpp
#include "SerialPortHandler.h"
#include <unistd.h>
#include <limits>
SerialPortHandler::SerialPortHandler(QString portname, QString baudrate, QObject *parent) :
QObject(parent)
{
timer = new QTimer(this);
sp = new QSerialPort(this);
if(!open(portname, baudrate)) {
qDebug() << sp->error() << sp->errorString();
exit(sp->error());
}
innotif = new QSocketNotifier(STDIN_FILENO, QSocketNotifier::Read, this);
connect(innotif, SIGNAL(activated(int)), this, SLOT(execmd()));
}
void SerialPortHandler::execmd()
{
qDebug() << "command received. analyzing...";
// qint64 nbr = -1, size = -1;
// qint32 timeout = -1;
// char * buff = 0;
// QTextStream in(stdin);
// QString ln = in.readAll();
// switch (analyze(ln)) {
// case READ:
// size = getNum(ln, 1);
// timeout = getNum(ln, 2);
// if(size > -1 && timeout > -1)
// nbr = read(buff, size, timeout);
// if(nbr > -1)
// reply(buff);
// break;
// default:
// break;
// }
}
bool SerialPortHandler::open(const QString &portname, const QString &baudrate)
{
sp->setPortName(portname);
if (!sp->open(QIODevice::ReadWrite) ||
!sp->setBaudRate(baudrate.toInt()) ||
!sp->setDataBits(QSerialPort::Data8) ||
!sp->setParity(QSerialPort::NoParity) ||
!sp->setStopBits(QSerialPort::OneStop) ||
!sp->setFlowControl(QSerialPort::NoFlowControl)) {
return false;
}
sp->clear();
qDebug() << "port openned successfully!";
return true;
}
//day light wont affect this timer so the system wont freeze
qint64 SerialPortHandler::read(char *buff, const qint64 size, const qint32 timeout)
{
qint64 numbytesread = -1;
timer->start(timeout);
while (true) {
if(timer->remainingTime() > 0) {
return -1;
}
if((sp->isReadable() && sp->bytesAvailable() > 0) ||
(sp->isReadable() && sp->waitForReadyRead(10))) {
numbytesread += sp->read(buff, size);
}
if(numbytesread < 0) {
return -1;
}
if(numbytesread == size) {
break;
}
}
return numbytesread;
}
void SerialPortHandler::notify()
{
}
QString SerialPortHandler::convertToCaseInsensitiveRegExp(QString str)
{
QString result;
for(int i = 0 ; i < str.size() ; ++i) {
result.append("[");
result.append(str.at(i).toLower());
result.append(str.at(i).toUpper());
result.append("]");
}
return result;
}
CommandType SerialPortHandler::analyze(const QString &line)
{
QString read, write;
read = convertToCaseInsensitiveRegExp("read");
write = convertToCaseInsensitiveRegExp("write");
if(line.contains(QRegExp(QString("^.*%1\\s+[1-9]\\d*\\s+[1-9]\\d*.*").arg(read)))) {
return READ;
}
return UNKNOWN;
}
qint64 SerialPortHandler::getNum(const QString& line, int index) {
QStringList args(line.split(QRegExp("\\s+")));
bool done;
qint64 size = args.at(index).toInt(&done, 10);
if(done) {
return size;
}
return -1;
}
void SerialPortHandler::reply(char * buff) {
QDataStream out(stdout);
out << buff;
}
main.cpp
#include <QCoreApplication>
#include <QDebug>
#include "SerialPortHandler.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
if(argc != 3) {
qDebug() << "usage:" << argv[0] << "port" << "baudrate";
} else {
SerialPortHandler *sph = new SerialPortHandler(argv[1], argv[2]);
}
return a.exec();
}
My parent process consists of the following:
ParentProcess.h
#ifndef PARENTPROCESS_H
#define PARENTPROCESS_H
#include <QObject>
#include <QtCore>
class ParentProcess : public QObject
{
Q_OBJECT
public:
explicit ParentProcess(QObject *parent = 0);
signals:
public slots:
private slots:
void sendRead();
void writeSomething();
void handleError(QProcess::ProcessError error);
private:
QProcess *p;
};
#endif // PARENTPROCESS_H
ParentProcess.cpp
#include "ParentProcess.h"
#include <QDebug>
ParentProcess::ParentProcess(QObject *parent) :
QObject(parent)
{
p = new QProcess(this);
connect(p, SIGNAL(readyReadStandardOutput()), this, SLOT(sendRead()));
connect(p, SIGNAL(readyReadStandardError()), this, SLOT(sendRead()));
connect(p, SIGNAL(started()), this, SLOT(writeSomething()));
connect(p, SIGNAL(error(QProcess::ProcessError)), this, SLOT(handleError(QProcess::ProcessError)));
QStringList args;
args << "/dev/ttyUSB0" << "115200";
p->start("/home/moki/Work/Programs/build-serialio-Desktop_Qt_5_3_0_GCC_64bit-Debug/serialio", args, QProcess::ReadWrite);
}
void ParentProcess::sendRead() {
qDebug() << "data:" << p->readAllStandardError() << p->readAllStandardOutput();
}
void ParentProcess::writeSomething() {
qDebug() << "writing";
QString cmd = "read 10 10000\n";
qint64 a = p->write(cmd.toStdString().c_str());
qDebug() << "wrote:" << a;
}
void ParentProcess::handleError(QProcess::ProcessError error)
{
switch (error) {
case QProcess::FailedToStart:
qDebug() << "failed to start";
break;
case QProcess::Crashed:
qDebug() << "crashed.";
break;
default:
break;
}
}
main.cpp
#include <QCoreApplication>
#include "ParentProcess.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
ParentProcess p;
return a.exec();
}
I have seen a couple of other answers in SO but none of them address my issue. As you can see my child process is not supposed to complete and exit. It will remain launched as long as the parent process wishes. Is it correct to use QProcess-launched processes this way?
I changed my code to the following and now it's working. But I haven't understood why this code works and my original one doesn't.
in the parent process source i changed the constructor as follows:
ParentProcess::ParentProcess(QObject *parent) :
QObject(parent)
{
p = new QProcess(this);
connect(p, SIGNAL(error(QProcess::ProcessError)), this, SLOT(handleError(QProcess::ProcessError)));
connect(p, SIGNAL(readyRead()), this, SLOT(sendRead()));
connect(p, SIGNAL(started()), this, SLOT(writeSomething()));
QStringList args;
args << "/dev/ttyUSB0" << "115200";
p->setProcessChannelMode(QProcess::MergedChannels);
p->start("/home/moki/Work/Programs/build-serialio-Desktop_Qt_5_3_0_GCC_64bit-Debug/serialio", args, QProcess::ReadWrite);
}
and the sendRead() function to this:
void ParentProcess::sendRead() {
int bytes = p->bytesAvailable();
qDebug() << "data:" << p->read(bytes);
}
finally, the writeSomething() to:
void ParentProcess::writeSomething() {
qDebug() << "gonna write.";
if(p->state() == QProcess::Running) {
qDebug() << "writing";
QString cmd = "read 10 10000\n";
qint64 a = p->write(cmd.toStdString().c_str());
qDebug() << "wrote:" << a << "bytes.";
}
}
and now it works. If anyone could please explain this to me, I would be really grateful.