I looked for examples everywhere, and I just can't seem to find a solution.
Issue
So : I have this receiving class called DataReceiver. Basically, I just want to receive data from a TCP Socket on the localhost (127.0.0.1) network. It seems that I can write w/ this socket (I checked w/ the Wireshark software) but it looks like the program does not receive anything.
I placed a qDebug() statement in the onDataReceived QT SLOT. This slot does not seem to be triggered at any moment. I tried editing the mTcpSocket->readyRead() QT SIGNAL manually, and the slot is correctly called upon, which means that the signal is not emitted because the QTcpSocket doesn't receive anything (or at least I this so).
My computer is a Mac w/ macOS High Sierra. I scanned open ports, and found that 51470 & 50911 are open. Those are the ports I tested.
DataReceiver class
The code for this class is the following :
datareceiver.h :
class DataReceiver : QObject
{
Q_OBJECT
public:
// Con/Destructors
explicit DataReceiver(QObject *parent = nullptr);
explicit DataReceiver(const QString &pSourceAddress,
const unsigned int &pSourcePort,
QObject *parent = nullptr);
~DataReceiver();
// Network Management
bool connectToHost(void);
// Getters
QVector<float> *getData(void) const;
QTcpSocket *getTcpSocket(void) const;
QString getSourceAddress(void) const;
unsigned int getSourcePort(void) const;
// Setters
void setData(const QVector<float> *pData);
void setSourceAddress(const QString &pSourceAddress);
void setSourcePort(const unsigned int &pSourcePort);
signals:
public slots:
void onConnect();
void onDisconnect();
void onBytesWritten(qint64 bytes);
void onDataReceived();
private slots:
private:
void decodeData(const QByteArray &pMessage);
QTcpSocket *mTcpSocket;
QString mSourceAddress;
unsigned int mSourcePort;
const unsigned int mDataSize = 30;
QVector<float> *mData;
};
And the implementation : datareceiver.cpp
// Con/Destructors
DataReceiver::DataReceiver(QObject *parent) :
QObject(parent),
mTcpSocket(new QTcpSocket(this)),
mSourceAddress("127.0.0.1"),
mSourcePort(51470),
mData(new QVector<float>)
{
for( unsigned int i = 0; i < mDataSize; i++)
mData->append(.0);
}
DataReceiver::DataReceiver(const QString &pSourceAddress,
const unsigned int &pSourcePort,
QObject *parent) :
QObject(parent),
mTcpSocket(new QTcpSocket(this)),
mSourceAddress(pSourceAddress),
mSourcePort(pSourcePort),
mData(new QVector<float>)
{
for( unsigned int i = 0; i < mDataSize; i++)
mData->append(.0);
}
DataReceiver::~DataReceiver(){
mTcpSocket->disconnectFromHost();
mTcpSocket->waitForDisconnected();
delete mTcpSocket;
delete mData;
}
// Network Management
bool DataReceiver::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)));
connect(mTcpSocket, SIGNAL(readyRead()), this, SLOT(onDataReceived()));
qDebug() << "connecting...";
//emit mTcpSocket->readyRead(); // For testing. We correctly trigger the onDataReceived slot
mTcpSocket->open(QAbstractSocket::ReadWrite);
mTcpSocket->connectToHost(getSourceAddress(), getSourcePort());
if(!mTcpSocket->waitForConnected(1000))
{
qDebug() << "Error: " << mTcpSocket->errorString();
return false;
}
mTcpSocket->write("Hello ?"); // Test
return true;
}
// Getters
QVector<float> *DataReceiver::getData(void) const{
return mData;
}
QTcpSocket *DataReceiver::getTcpSocket(void) const{
return mTcpSocket;
}
QString DataReceiver::getSourceAddress(void) const{
return mSourceAddress;
}
unsigned int DataReceiver::getSourcePort(void) const{
return mSourcePort;
}
// Setters
void DataReceiver::setData(const QVector<float> *pData){
// Not yet implemented
Q_UNUSED(pData);
}
void DataReceiver::setSourceAddress(const QString &pSourceAddress){
mSourceAddress = pSourceAddress;
}
void DataReceiver::setSourcePort(const unsigned int &pSourcePort){
mSourcePort = pSourcePort;
}
// Public Slots
void DataReceiver::onConnect(){
qDebug() << "connected...";
}
void DataReceiver::onDisconnect(){
qDebug() << "disconnected...";
}
void DataReceiver::onBytesWritten(qint64 bytes){
qDebug() << bytes << " bytes written...";
}
// Private Slots
void DataReceiver::onDataReceived(){
// Not yet implemented, code is for testing
qDebug() << "onDataReceived called !";
while(mTcpSocket->bytesAvailable()){
qInfo() << mTcpSocket->read(mTcpSocket->bytesAvailable());
qDebug() << mTcpSocket->readAll();
}
}
// Private Methods
void DataReceiver::decodeData(const QByteArray &pMessage){
// Not yet implemented
Q_UNUSED(pMessage);
}
The mData/mDataSize are here for future usage, and are thus to be ignored here.
Sender
To send data, I tried using netcat :
cat testfile.txt | nc 127.0.0.1 51470
I also created a DataSender class, w/ the same structure as the DataReceiver class.
According to Wireshark, both methods seem to write data via TCP.
DataSender class
datasender.h
class DataSender : QObject
{
Q_OBJECT
public:
// Con/Destructors
explicit DataSender(QObject *parent = nullptr);
~DataSender();
// Network Management
bool connectToHost(void);
void sendData(void) const;
// Getters
QString getDestinationAddress(void) const;
unsigned int getDestinationPort(void) const;
// Setters
void setDestinationAddress(const QString &pDestinationAddress);
void setDestinationPort(const unsigned int &pDestinationPort);
signals:
public slots:
void onConnect();
void onDisconnect();
void onBytesWritten(qint64 bytes);
void onDataReceived();
private:
QTcpSocket *mTcpSocket;
QString mDestinationAddress;
unsigned int mDestinationPort;
};
datasender.cpp
DataSender::DataSender(QObject *parent) :
QObject(parent),
mTcpSocket(new QTcpSocket(this)),
mDestinationAddress("127.0.0.1"),
mDestinationPort(50911)
{
}
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)));
connect(mTcpSocket, SIGNAL(readyRead()), this, SLOT(onDataReceived()));
qDebug() << "connecting...";
mTcpSocket->setSocketOption(QAbstractSocket::KeepAliveOption, true);
mTcpSocket->connectToHost(getDestinationAddress(), getDestinationPort());
if(!mTcpSocket->waitForConnected(1000))
{
qDebug() << "Error: " << mTcpSocket->errorString();
return false;
}
return true;
}
void DataSender::sendData(void) const{
QByteArray lData("Hello, this is DataSender ! Do you copy ? I repeat, do you copy ?");
QByteArray lTemp;
QDataStream lData2(&lTemp, QIODevice::ReadWrite);
lData2 << lData.size();
if(mTcpSocket->state() == QAbstractSocket::ConnectedState)
{
mTcpSocket->write(lTemp); //write size of data
mTcpSocket->write(lData); //write the data itself
mTcpSocket->waitForBytesWritten();
}
}
// 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::onDataReceived(){
// Not yet implemented, code is for testing
qDebug() << "onDataReceived called !";
//while(mTcpSocket->bytesAvailable()){
//qInfo() << mTcpSocket->read(mTcpSocket->bytesAvailable());
//qDebug() << mTcpSocket->readAll();
//}
}
Client side Main
// Main routine ---------------------------------
int main(int argc, char **argv){
// Initializing application.
QApplication lApplication(argc, argv);
CLIENT::DataReceiver dataReceiver;
dataReceiver.connectToHost();
return lApplication.exec();
}
Server side Main
// Main routine ---------------------------------
int main(int argc, char **argv){
QApplication lApplication(argc, argv);
SERVER::DataSender lDataSender;
lDataSender.connectToHost();
lDataSender.sendData();
return lApplication.exec();
}
Behavior
Basically, when I run the DataReceiver class in the main, I get the following behavior :
connecting...
connected...
7 bytes written... # This is the write("Hello ?") I inserted just for testing
# "connected..." may occur after "7 bytes written...",
# I don't remember, I am not in front of my computer right now.
Conclusion
I believe I have simply forgotten something important, or that there is something I do not know of. This is a personal project, so a little exterior insight would be welcome !
Thank you very much !
Clovel
A QTcpSocket can communicate with another QTcpSocket, but in order for this to happen, an initial connection must be made, utilising a client-server model.
It's not possible for a QTcpSocket to listen for an incoming connection. Instead, QTcpServer can be used (or QLocalServer, if using QLocalSocket).
The QTcpSocket is set to listen on a port for incoming connections and when a connection is made, will emit a signal to notify of the newConnection. Calling nextPendingConnection returns the QTcpSocket for the server side, allowing it to communicate with client from the incoming connection.
So this is OP, and here is the solution I implemented. I'd like to thank #TheDarkKnight & #G.M. for their clarifications.
Solution
Instead of using a QTcpSocket for my DataReceiver class, I used a combination of QTcpSocket AND QTcpServer. Basically, the QTcpServer listens for new connections & set the QTcpSocket when a connection is made.
Then, the class simply connects the QTcpSocket signal readyRead() to a personal slot. Here is the implementation of the class :
DataReceiver class
datareceiver.h
class DataReceiver : QObject
{
Q_OBJECT
public:
// Con/Destructors
explicit DataReceiver(QObject *parent = nullptr);
explicit DataReceiver(const QString &pSourceAddress,
const unsigned int &pSourcePort,
QObject *parent = nullptr);
~DataReceiver();
// Getters
QVector<float> *getData(void) const;
QTcpServer *getTcpServer(void) const;
QString getSourceAddress(void) const;
unsigned int getSourcePort(void) const;
// Setters
void setData(const QVector<float> *pData);
void setSourceAddress(const QString &pSourceAddress);
void setSourcePort(const unsigned int &pSourcePort);
signals:
public slots:
void onConnect();
void onDisconnect();
void onBytesWritten(qint64 bytes);
void onDataReceived();
void onNewConnection();
private:
void decodeData(const QByteArray &pMessage);
QTcpServer *mTcpServer;
QTcpSocket *mTcpSocket;
QString mSourceAddress;
unsigned int mSourcePort;
const unsigned int mDataSize = 30;
QVector<float> *mData;
};
datareceiver.cpp
// Con/Destructors
DataReceiver::DataReceiver(QObject *parent) :
QObject(parent),
mTcpServer(new QTcpServer(this)),
mSourceAddress("127.0.0.1"),
mSourcePort(51470),
mData(new QVector<float>)
{
for( unsigned int i = 0; i < mDataSize; i++)
mData->append(.0);
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),
mData(new QVector<float>)
{
for( unsigned int i = 0; i < mDataSize; i++)
mData->append(.0);
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(){
delete mTcpServer;
delete mData;
}
// Getters
QVector<float> *DataReceiver::getData(void) const{
return mData;
}
QTcpServer *DataReceiver::getTcpServer(void) const{
return mTcpServer;
}
QString DataReceiver::getSourceAddress(void) const{
return mSourceAddress;
}
unsigned int DataReceiver::getSourcePort(void) const{
return mSourcePort;
}
// Setters
void DataReceiver::setData(const QVector<float> *pData){
// Not yet implemented
Q_UNUSED(pData);
}
void DataReceiver::setSourceAddress(const QString &pSourceAddress){
mSourceAddress = pSourceAddress;
}
void DataReceiver::setSourcePort(const unsigned int &pSourcePort){
mSourcePort = pSourcePort;
}
// 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 !";
while(mTcpSocket->bytesAvailable()){
qInfo() << mTcpSocket->read(mTcpSocket->bytesAvailable());
qDebug() << mTcpSocket->readAll();
}
}
void DataReceiver::onNewConnection(){
qDebug() << "onNewConnection called !";
mTcpSocket = mTcpServer->nextPendingConnection();
connect(mTcpSocket, SIGNAL(readyRead()), this, SLOT(onDataReceived()));
connect(mTcpSocket, SIGNAL(disconnected()), this, SLOT(onDisconnect()));
}
// Private Methods
void DataReceiver::decodeData(const QByteArray &pMessage){
// Not yet implemented
Q_UNUSED(pMessage);
}
Related
How are you solving signals/slots mechanism between two classes, if you do not make an object from class(only inherit from class) etc? QTimer, QSerialPort, which is the source of SIGNAL, and in the second class you make connection?
Is such an approach even possible?
In my case. I have two classes usb2can_driver and canview. The usb2can_driver inherit from QSerialPort, which inherit QIODevice contained SIGNAL(readyRead()). This is used in the canview for connection with handler subroutine read_u2c()
I know, in the code is a lot of garbage from testing.
In the canview.cpp void CANview::on_connectPort_released() is made connection, and in the usb2can_driver.cpp int USB2CAN_driver::connectToPort(QString portName) is part of inherit from QSerialPort.
I will be pleasure for every answer. If you think, that question is posed incorrectly, please give me feedback.
usb2can_driver.h
#ifndef USB2CAN_DRIVER_H
#define USB2CAN_DRIVER_H
#include "QSerialPort"
#include "QTimer"
#include "QObject"
#include "QSignalSpy"
class USB2CAN_driver : public QSerialPort
{
Q_OBJECT;
public:
USB2CAN_driver();
//virtual ~USB2CAN_driver();
//QSerialPort *port_USB2CAN = new QSerialPort();
int temporary_init_Counter = 0;
int init();
void USB_LoopBack();
void Boot_Mode();
void Config_Mode();
void Normal_Mode();
void LoopBack_Mode();
QByteArray Get_Mode();
void WriteReg(QByteArray regAdress, QByteArray value[]);
QByteArray WriteCMD(QByteArray CMD_name, QByteArray value);
QByteArray ReadReg(QByteArray regAdress);
QString portName;
int connectToPort(QString portName);
int disconnectedFromPort();
QTimer *tim;
int tim_counter = 0;
public: signals:
void readyRead();
private slots:
QByteArray read_USB2CAN();
void initSend();
//void timEvent();
};
#endif // USB2CAN_DRIVER_H
usb2can_driver.cpp
#include "USB2CAN_define.h"
#include "QSerialPort"
#include "QSerialPort"
#include "QObject"
#include "QDebug"
#include <QSignalSpy>
USB2CAN_driver::USB2CAN_driver()
{
//USB2CAN_driver:: = new QSerialPort();
//USB2CAN_driver::Baud9600;
//USB2CAN_driver::AllDirections;
//qDebug() << "Open port" << USB2CAN_driver::open(QIODevice::ReadWrite);
}
/*
USB2CAN_driver::~USB2CAN_driver(){
QObject::disconnect(USB2CAN_driver::,SIGNAL(readyRead()),USB2CAN_driver::,SLOT(QByteArray read_USB2CAN()));
}
*/
int USB2CAN_driver::connectToPort(QString portName){
//port_USB2CAN.setPortName(portName);
USB2CAN_driver::setPortName(portName);
USB2CAN_driver::setBaudRate(QSerialPort::Baud9600,QSerialPort::AllDirections);
USB2CAN_driver::setPortName(portName);
//Reimplemented separately as signal of driver. !!!
//qDebug() << "connect S&S in the driver, status: " << QObject::connect(this,SIGNAL(readyRead),this,SLOT(read_USB2CAN));
//qDebug() << "connect S&S in the driver, status: " << connect(this,SIGNAL(readyRead()),this,SLOT(read_USB2CAN()));
//QSignalSpy spy(this, SIGNAL(readyRead()));
//qDebug() << "from driver" << spy.wait(5000) << "----" << spy.signal();
tim = new QTimer;
return USB2CAN_driver::open(QIODevice::ReadWrite);
}
/*
void USB2CAN_driver::timEvent(){
qDebug() << "Tim" << tim_counter++;
if(tim_counter >= 5){
tim_counter = 0;
tim->stop();
}
}
*/
int USB2CAN_driver::disconnectedFromPort(){
//QObject::disconnect(this,SIGNAL(readyRead()),this,SLOT(read_USB2CAN()));
USB2CAN_driver::close();
if(USB2CAN_driver::isOpen()){
return 1;
}
else{
qDebug() << "------------------Port is diconected-----------------";
return 0;
}
}
void USB2CAN_driver::USB_LoopBack(){
}
void USB2CAN_driver::Boot_Mode(){
}
void USB2CAN_driver::Config_Mode(){
}
void USB2CAN_driver::Normal_Mode(){
}
void USB2CAN_driver::LoopBack_Mode(){
}
QByteArray USB2CAN_driver::Get_Mode(){
while(!USB2CAN_driver::waitForBytesWritten(300)){
USB2CAN_driver::write(getMode);
}
return USB2CAN_driver::readAll(); //In progress...
}
void USB2CAN_driver::WriteReg(QByteArray regAdress, QByteArray value[]){
int length = regAdress.length() + value->length();
QByteArray len;
len.setNum(length);
QByteArray sendVal[] = { writeReg, len, regAdress, *value };
QByteArray sendData;
sendData.fromRawData(*sendVal,sizeof (sendVal));
while(!USB2CAN_driver::waitForBytesWritten(300)){
USB2CAN_driver::write(sendData);
}
}
QByteArray USB2CAN_driver::WriteCMD(QByteArray CMD_name, QByteArray value){
}
QByteArray USB2CAN_driver::ReadReg(QByteArray regAdress){
}
int USB2CAN_driver::init(){
}
QByteArray USB2CAN_driver::read_USB2CAN(){
qDebug() <<"From driver RX" << USB2CAN_driver::readAll();
return USB2CAN_driver::readAll();
}
void USB2CAN_driver::initSend(){
}
canview.h
#define CANVIEW_H
#include <QDialog>
#include <usb2can_driver.h>
namespace Ui {
class CANview;
}
class CANview : public QDialog
{
Q_OBJECT
public:
explicit CANview(QWidget *parent = nullptr);
~CANview();
USB2CAN_driver *u2c;
QTimer *time;
private: signals:
friend void USB2CAN_driver::readyRead();
private slots:
void on_connectPort_released();
void on_pushButton_released();
QByteArray read_u2c();
void timerSubrutine();
private:
Ui::CANview *ui;
};
#endif // CANVIEW_H
canview.cpp
#include "ui_canview.h"
#include "QSignalSpy"
CANview::CANview(QWidget *parent) : QDialog(parent),ui(new Ui::CANview)
{
ui->setupUi(this);
u2c = new USB2CAN_driver;
}
CANview::~CANview()
{
delete ui;
}
//connect fcn
void CANview::on_connectPort_released()
{
if(u2c->isOpen()){
u2c->disconnectedFromPort();
}
else{
u2c->connectToPort(ui->inputNamePort->text());
qDebug() << "Connect rx task, status: " << connect(???,SIGNAL(readyRead()),this,SLOT(read_u2c()));
connect(u2c->tim,SIGNAL(timeout()),this,SLOT(timerSubrutine()));
u2c->tim->start(800);
}
//Controll of opened/close port
if(u2c->isOpen()){
ui->connectPort->setCheckState(Qt::CheckState::Checked);
}
else{
ui->connectPort->setCheckState(Qt::CheckState::Unchecked);
}
}
//Send function
void CANview::on_pushButton_released()
{
u2c->write(ui->TX_textBrowser->toPlainText().toLatin1(),static_cast<int>(ui->TX_textBrowser->toPlainText().length()));
qDebug() << "Send: " << static_cast<int> (ui->TX_textBrowser->toPlainText().length());
QSignalSpy spy(u2c,SIGNAL(readyRead()));
qDebug() << spy.signal() << spy.signalsBlocked() << spy.isValid();
}
QByteArray CANview::read_u2c(){
qDebug() << "RX:" << u2c->readAll();
ui->RX_textBrowser_2->setPlainText(u2c->readAll());
return u2c->readAll();
}
void CANview::timerSubrutine(){
qDebug() << "TimerEvent" << u2c->tim_counter++;
if(u2c->tim_counter >= 5){
u2c->tim->stop();
}
}```
It is impossible to connect classes as mentioned by #Scheff's Cat.
The solution is do not use inheritance from QSerialPort in the usb2can_driver. If I want connect signal of QSerialPort with slot (which is part of second class), I had to create a object from QSerialPort in the constructor of USB2CAN_driver.
This object to allow use signal/slot mechanism.
So in short: USB2CAN_driver:: was replaced by object port_USB2CAN
For the connection in the second class (canview), i used this syntax:
connect(u2c->port_USB2CAN,SIGNAL(readyRead()),this,SLOT(read_u2c()));
Thank to Scheff's Cat, your comment was helpfully. This solution is working, but if somebody see the non-standard syntax please warning me.
I'm making a server-client system for a client placed within a loop to continuously query a predetermined set of information from a server. I've cooked up a bit of code from what I could understand about how the TCP implementation of the Qt framework works but I'm not sure if what I have written is the right way to do it.
At first, I made an enum with a bunch of QByteArray variables in the client QTcp class to pass as a query from the client side to the server as shown below
enum datamap
{
QByteArray data1 = 1;
QByteArray data2 = 2;
QByteArray data3 = 3;
// and so on...
};
Then I make the function that takes in an enum datamap variable to pass to the server and a variable to hold the current request(to avoid mixup between data received for the wrong request) as shown below
datamap current_request = 0;
int client::setDataToGet(datamap& data)
{
if(socket->state() == QAbstractSocket::ConnectedState)
{
current_request = data;
socket->write(data);
return socket->waitForBytesWritten();
}
else
return -1;
}
After this, I create the readyRead() slot that connects to the readyread signal to handle responses from the server and send to functions that will display the received data in the respective text box based on the current_request variable as shown below
void client::readyRead()
{
QTcpSocket *m_socket = static_cast<QTcpSocket*>(sender());
while(m_socket->bytesAvailable() > 0)
{
QByteArray buf = socket->readAll();
}
switch(current_request):
case 1:
dispToTextBox1(buf);
case 2:
dispToTextBox2(buf);
case 3:
dispToTextBox3(buf);
// and so on....
}
Now, for the server side, I make the readyRead() slot that connects to the readyread() signal from a socket listener called in the newConnection() function.
This takes in the handle from the client and accordingly is supposed to return back data associated with the handle. The code for the slot is as follows
void server::readyRead()
{
QTcpSocket *m_socket = static_cast<QTcpSocket*>(sender());
while(m_socket->bytesAvailable() > 0)
{
QByteArray buf = m_socket->readAll();
}
switch(buf):
case 1:
data = collectDatafromStream1();
m_socket->write(data); m_socket->flush();
case 2:
data = collectDatafromStream2();
m_socket->write(data); m_socket->flush();
case 3:
data = collectDatafromStream3();
m_socket->write(data); m_socket->flush();
//and so on.....
}
Could someone please verify if this is the right way to do this or if there is a better alternative for handling the task.
Got the concept model to work with a few changes. Used the response given by "sashoalm" in this question to pass the data using QdataStream and used signals & slots to cycle through the read-write sequence for each request type.
my server class is as follows
tcpserver.h
#ifndef TCPSERVER_H
#define TCPSERVER_H
#include<qt5/QtNetwork/QTcpServer>
#include<qt5/QtNetwork/QTcpSocket>
#include<qt5/QtCore/QObject>
class TCPServer : public QObject
{
Q_OBJECT
public:
explicit TCPServer(QObject *parent = nullptr);
signals:
void dataReceived(QByteArray);
private slots:
void newConnection();
void disconnected();
void readyRead();
private:
QTcpServer *server;
QTcpSocket *socket;
QHash<QString, int> reverse_hash;
};
#endif // TCPSERVER_H
tcpserver.cpp
#include <iostream>
#include "tcpserver.h"
#include <qt5/QtCore/QDataStream>
#include <qt5/QtCore/QBuffer>
#include <qt5/QtCore/QString>
class BlockWriter
{
public:
BlockWriter(QIODevice *io)
{
buffer.open(QIODevice::WriteOnly);
this->io = io;
_stream.setVersion(QDataStream::Qt_4_8);
_stream.setDevice(&buffer);
_stream << quint64(0);
}
~BlockWriter()
{
_stream.device()->seek(0);
_stream << static_cast<quint64>(buffer.size());
io->write(buffer.buffer());
}
QDataStream &stream()
{
return _stream;
}
private:
QBuffer buffer;
QDataStream _stream;
QIODevice *io;
};
class BlockReader
{
public:
BlockReader(QIODevice *io)
{
buffer.open(QIODevice::ReadWrite);
_stream.setVersion(QDataStream::Qt_4_8);
_stream.setDevice(&buffer);
qint64 blockSize;
readMax(io, sizeof(blockSize));
buffer.seek(0);
_stream >> blockSize;
readMax(io, blockSize);
buffer.seek(sizeof(blockSize));
}
QDataStream& stream()
{
return _stream;
}
private:
void readMax(QIODevice *io, qint64 n)
{
while (buffer.size() < n) {
buffer.write(io->read(n - buffer.size()));
}
}
QBuffer buffer;
QDataStream _stream;
};
TCPServer::TCPServer(QObject *parent) : QObject(parent)
{
server = new QTcpServer(this);
connect(server, SIGNAL(newConnection()), SLOT(newConnection()));
qDebug() << "Listening:" << server->listen(QHostAddress::Any, 5404);
reverse_hash.insert("data1", 1);
reverse_hash.insert("data2", 2);
}
void TCPServer::newConnection()
{
while (server->hasPendingConnections())
{
qDebug()<<"incoming connection!";
socket = server->nextPendingConnection();
connect(socket, SIGNAL(readyRead()), SLOT(readyRead()));
connect(socket, SIGNAL(disconnected()), SLOT(disconnected()));
}
}
void TCPServer::disconnected()
{
qDebug() << "disconnected!";
disconnect(socket, SIGNAL(readyRead()));
disconnect(socket, SIGNAL(disconnected()));
socket->deleteLater();
}
void TCPServer::readyRead()
{
qDebug() << "Read!";
QString data;
BlockReader(socket).stream() >> data;
qDebug() <<"received data request: " << data;
switch(reverse_hash.value(data))
{
case 1: //call sequence to respond to request.(write to data)
qDebug() << "responding go data1 request!";
break;
case 2://call sequence to respond to request.(write to data)
qDebug() << "responding go data2 request!";
break;
}
BlockWriter(socket).stream()<<data;
socket->flush();
}
my client GUI class is as follows
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtNetwork>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
QTcpSocket *socket;
QHash<int, QString> hash;
int current_slot, starting_slot, ending_slot;
signals:
void dataSet();
public slots:
void connectToHost();
void connected();
void disconnected();
void setDatatoGet();
void getData();
//bool writeData(QByteArray data);
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <iostream>
class BlockWriter
{
public:
BlockWriter(QIODevice *io)
{
buffer.open(QIODevice::WriteOnly);
this->io = io;
_stream.setVersion(QDataStream::Qt_4_8);
_stream.setDevice(&buffer);
_stream << quint64(0);
}
~BlockWriter()
{
_stream.device()->seek(0);
_stream << static_cast<quint64>(buffer.size());
io->write(buffer.buffer());
}
QDataStream &stream()
{
return _stream;
}
private:
QBuffer buffer;
QDataStream _stream;
QIODevice *io;
};
class BlockReader
{
public:
BlockReader(QIODevice *io)
{
buffer.open(QIODevice::ReadWrite);
_stream.setVersion(QDataStream::Qt_4_8);
_stream.setDevice(&buffer);
qint64 blockSize;
readMax(io, sizeof(blockSize));
buffer.seek(0);
_stream >> blockSize;
readMax(io, blockSize);
buffer.seek(sizeof(blockSize));
}
QDataStream& stream()
{
return _stream;
}
private:
void readMax(QIODevice *io, qint64 n)
{
while (buffer.size() < n) {
buffer.write(io->read(n - buffer.size()));
}
}
QBuffer buffer;
QDataStream _stream;
};
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
starting_slot = 1;
current_slot = starting_slot;
ending_slot = 2;
ui->setupUi(this);
ui->status_label->setStyleSheet("background-color:red;");
ui->receive_btn->setEnabled(false);
socket = new QTcpSocket(this);
connectToHost();
connect(ui->conn_btn, SIGNAL(clicked()), this, SLOT(connectToHost()));
connect(ui->receive_btn, SIGNAL(clicked()), this, SLOT(setDatatoGet()));
connect(this, SIGNAL(dataSet()), this , SLOT(setDatatoGet()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::connectToHost()
{
socket->deleteLater();
socket = new QTcpSocket(this);
socket->connectToHost(QHostAddress("192.168.0.127"), 5404);
connect(socket, SIGNAL(connected()), this, SLOT(connected()));
connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected()));
//ADD to hash table
hash.insert(1, "data1");
hash.insert(2, "data2");
}
void MainWindow::connected()
{
ui->status_label->setStyleSheet("background-color:green;");
ui->receive_btn->setEnabled(true);
connect(socket, SIGNAL(readyRead()),this, SLOT(getData()));
}
void MainWindow::disconnected()
{
ui->status_label->setStyleSheet("background-color:red;");
ui->receive_btn->setEnabled(false);
disconnect(socket, SIGNAL(readyRead()),this, SLOT(getData()));
}
void MainWindow::setDatatoGet()
{
if(current_slot == ending_slot + 1)
{
current_slot = starting_slot;
}
qDebug() <<"calling request data slot " << current_slot;
BlockWriter(socket).stream() << hash.value(current_slot);
socket->flush();
current_slot++;
}
void MainWindow::getData()
{
QString data;
BlockReader(socket).stream() >> data;
//qDebug() <<"received response, current received data is for slot "<< data <<"and current number is" << current_slot;
switch (current_slot - 1)
{
case 1:
//display in respective label
qDebug() << "display data1 to label!";
break;
case 2:
//display in respective label
qDebug() << "display data2 to label!";
break;
}
emit dataSet();
}
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 want to communicate via the serial port in Qt (Qt 5.4.0, Ubuntu 14.04.3), and for decreasing the workload I want to shift the communication part into a second thread. Thus I created the following files:
serial_controller.cpp:
#include "serial_controller.h"
serial_controller_worker::serial_controller_worker(const QString &portname, int waitTimeout, int BaudRate)
{
this->portName = portname;
this->waitTimeout = waitTimeout;
this->baudrate = BaudRate;
this->serial.setPortName(this->portName);
this->serial.setBaudRate(this->baudrate);
if (!serial.open(QIODevice::ReadWrite))
{
emit error(tr("Can't open %1, error code %2").arg(portName).arg(serial.error()));
qDebug() << tr("Can't open %1, error code %2").arg(portName).arg(serial.error());
return;
}
else
{
emit error(tr("Opened %1").arg(portName));
qDebug() << tr("Opened %1").arg(portName);
}
}
serial_controller_worker::~serial_controller_worker()
{
this->serial.close();
}
void serial_controller_worker::process_data()
{
bool newData = false;
bool run = false;
this->mutex.lock();
newData = this->sendNewData;
run = this->recvLoop;
this->mutex.unlock();
if(run == false)
{
qDebug() << "Run is false, returning!";
return;
}
else
{
if(newData == true)
{
qDebug() << "TransAction started!";
QByteArray requestData = request.toLocal8Bit();
qDebug() << "Writing data: " << requestData;
serial.write(requestData);
qDebug() << "Data written";
if(serial.waitForBytesWritten(waitTimeout))
{
if(serial.waitForReadyRead(waitTimeout))
{
qDebug() << "Waiting for data!";
QByteArray responseData = serial.readAll();
while(serial.waitForReadyRead(10))
responseData += serial.readAll();
QString response(responseData);
QByteArray response_arr = response.toLocal8Bit();
qDebug() << "Response is: " << response_arr.toHex();
emit this->response(response);
}
else
{
qDebug() << "Wait read response timeout";
emit this->timeout(tr("Wait read response timeout %1").arg(QTime::currentTime().toString()));
}
}
else
{
qDebug() << "Wait write request timeout!";
emit this->timeout(tr("Wait write request timeout %1").arg(QTime::currentTime().toString()));
}
mutex.lock();
this->sendNewData = false;
mutex.unlock();
}
QThread::msleep(10);
this->process_data();
}
}
void serial_controller_worker::transaction(const QString &request)
{
mutex.lock();
this->sendNewData = true;
this->recvLoop = true;
this->request = request;
mutex.unlock();
this->process_data();
}
//Serial_controller functions
serial_controller::serial_controller(const QString &portName, int waitTimeout, int BaudRate)
{
serial_controller_worker *newWorker = new serial_controller_worker(portName, waitTimeout, BaudRate);
newWorker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, newWorker, &QObject::deleteLater);
connect(this, &serial_controller::newTransaction, newWorker, &serial_controller_worker::transaction);
connect(newWorker, &serial_controller_worker::response, this, &serial_controller::response_slot);
workerThread.start();
}
serial_controller::~serial_controller()
{
workerThread.quit();
workerThread.wait();
}
void serial_controller::transaction(const QString &request)
{
emit this->newTransaction(request);
}
void serial_controller::response_slot(QString response)
{
emit this->response(response);
}
serial_controller.h:
#include <QObject>
#include <QThread>
#include <QVector>
#include <memory>
#include <QtSerialPort/QtSerialPort>
class serial_controller_worker: public QObject
{
Q_OBJECT
private:
QString portName;
QString request;
int waitTimeout;
QMutex mutex;
QWaitCondition cond;
int baudrate;
QSerialPort serial;
bool quit;
bool sendNewData = false;
bool recvLoop = false;
public slots:
void transaction(const QString &request);
signals:
void response(QString s);
void error(const QString &s);
void timeout(const QString &s);
public:
serial_controller_worker(const QString &portname, int waitTimeout, int BaudRate);
~serial_controller_worker();
void process_data(void);
};
class serial_controller: public QObject
{
Q_OBJECT
private:
QThread workerThread;
QString portName;
QString request;
int waitTimeout;
QMutex mutex;
QWaitCondition cond;
int baudrate;
QSerialPort serial;
public:
serial_controller(const QString &portName, int waitTimeout, int BaudRate);
~serial_controller();
public slots:
void transaction(const QString &request);
void response_slot(QString response);
signals:
void newTransaction(const QString &request);
void response(QString s);
void error(const QString &s);
void timeout(const QString &s);
};
Now I have the problem that
a) when calling serial_controller_worker::process_data(), I get the output
Writing data: "..."
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QSerialPort(0x2533e60), parent's thread is QThread(0x2486850), current thread is QThread(0x25341e0)
Data written
QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
Then I get the answer, but afterwards I am not able to call serial_controller_worker::transaction() anymore, I simply get no notice that it is executed. Why? What am I doing wrong here?
Edit 1: Solution to problem 1 is:
Replacing
QSerialPort serial;
with
QSerialPort *serial;
serial = new QSerialPort(this);
Still the second problem is unsolved.
Ok, found the answer.
The solution to the first problem is:
I have to replace
QSerialPort serial;
with
QSerialPort *serial;
serial = new QSerialPort(this);
The solution to the second problem:
Remove the line
this->process_data();
from the function process_data()
I created a small server/client application, and for testing I put the server/client function into a separate application.
The main client functions are
Client::Client(QString purpose) : networkSession(0)
{
Client::purpose = purpose;
tcpSocket = new QTcpSocket;
Client::blockSize = 0;
connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(readData()));
connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)),
this, SLOT(displayError(QAbstractSocket::SocketError)));
QNetworkConfigurationManager manager;
if (manager.capabilities() & QNetworkConfigurationManager::NetworkSessionRequired)
{
// Get saved network configuration
QSettings settings(QSettings::UserScope, QLatin1String("QtProject"));
settings.beginGroup(QLatin1String("QtNetwork"));
const QString id = settings.value(QLatin1String("DefaultNetworkConfiguration")).toString();
settings.endGroup();
// If the saved network configuration is not currently discovered use the system default
QNetworkConfiguration config = manager.configurationFromIdentifier(id);
if ((config.state() & QNetworkConfiguration::Discovered) !=
QNetworkConfiguration::Discovered) {
config = manager.defaultConfiguration();
}
networkSession = new QNetworkSession(config, this);
connect(networkSession, SIGNAL(opened()), this, SLOT(sessionOpened()));
}
qDebug() << "Client set up, waiting";
}
void Client::connectToServer(QString ipAddr, quint32 port)
{
qDebug() << "Connecting to Host on port " << port << ' ' << (quint16)port;
tcpSocket->connectToHost(ipAddr, port);
emit this->connectionResult((tcpSocket->state() == QAbstractSocket::UnconnectedState)?false:true);
if (tcpSocket->waitForConnected(1000))
qDebug("Connected!");
qDebug() << "Am I connected" << tcpSocket->state();
std::cout << "Am I not connected" << tcpSocket->state();
}
and the server-functions:
Server::Server(QString ipAddr, quint32 port, QString purpose)
: tcpServer(0), networkSession(0)
{
Server::clientConnection = NULL;
Server::purpose = purpose;
Server::port = port;
QNetworkConfigurationManager manager;
if (manager.capabilities() & QNetworkConfigurationManager::NetworkSessionRequired) {
// Get saved network configuration
QSettings settings(QSettings::UserScope, QLatin1String("QtProject"));
settings.beginGroup(QLatin1String("QtNetwork"));
const QString id = settings.value(QLatin1String("DefaultNetworkConfiguration")).toString();
settings.endGroup();
// If the saved network configuration is not currently discovered use the system default
QNetworkConfiguration config = manager.configurationFromIdentifier(id);
if ((config.state() & QNetworkConfiguration::Discovered) !=
QNetworkConfiguration::Discovered) {
config = manager.defaultConfiguration();
}
networkSession = new QNetworkSession(config, this);
connect(networkSession, SIGNAL(opened()), this, SLOT(sessionOpened()));
//statusLabel->setText(tr("Opening network session."));
networkSession->open();
} else {
sessionOpened();
}
//connect(tcpServer, SIGNAL(newConnection()), this, SLOT(sendFortune()));
connect(tcpServer, SIGNAL(newConnection()), this, SLOT(openNewConnection()));
//connect(tcpServer, &QTcpServer::newConnection, this, &Server::openNewConnection);
}
void Server::sessionOpened()
{
// Save the used configuration
if (networkSession) {
QNetworkConfiguration config = networkSession->configuration();
QString id;
if (config.type() == QNetworkConfiguration::UserChoice)
id = networkSession->sessionProperty(QLatin1String("UserChoiceConfiguration")).toString();
else
id = config.identifier();
QSettings settings(QSettings::UserScope, QLatin1String("QtProject"));
settings.beginGroup(QLatin1String("QtNetwork"));
settings.setValue(QLatin1String("DefaultNetworkConfiguration"), id);
settings.endGroup();
}
tcpServer = new QTcpServer(this);
if (!tcpServer->listen(QHostAddress::Any, Server::port)) {
return;
}
qDebug() << "Server listening on: " << tcpServer->serverPort();
//! [0]
QString ipAddress;
QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses();
// use the first non-localhost IPv4 address
for (int i = 0; i < ipAddressesList.size(); ++i) {
if (ipAddressesList.at(i) != QHostAddress::LocalHost &&
ipAddressesList.at(i).toIPv4Address()) {
ipAddress = ipAddressesList.at(i).toString();
break;
}
}
// if we did not find one, use IPv4 localhost
if (ipAddress.isEmpty())
ipAddress = QHostAddress(QHostAddress::LocalHost).toString();
//! [1]
}
void Server::openNewConnection(void)
{
qDebug() << "New conn incoming!";
Server::clientConnection = tcpServer->nextPendingConnection();
QVariant ipAddr_QVar(clientConnection->peerAddress().toString());
qDebug() << "Got new connection!";
emit gotNewConnection(ipAddr_QVar);
}
The main problem here is that even if I get a "Connected" from
if (tcpSocket->waitForConnected(1000))
qDebug("Connected!");
qDebug() << "Am I connected" << tcpSocket->state();
in the client function, but the server function openNewConnection() never gets called. Why? How can I find the bug?
If a minimal working example is necessary, I can provide the whole code, but here I just wanted to provide the most important functions.
Edit:
client.h:
#ifndef CLIENT_H
#define CLIENT_H
//#include <QDialog>
#include <iostream>
#include <QTcpSocket>
class QComboBox;
class QDialogButtonBox;
class QLabel;
class QLineEdit;
class QPushButton;
class QTcpSocket;
class QNetworkSession;
class Client : public QObject
{
Q_OBJECT
private:
QTcpSocket *tcpSocket;
QString currentFortune;
quint16 blockSize;
QPair<QString, QPair<QString, QVariant> > data;
QNetworkSession *networkSession;
QString purpose;
signals:
void gotData(QPair<QString, QPair<QString, QVariant> >);
void noConnection(void);
void connectionResult(bool);
void isDisconnect(bool);
public slots:
void displayError(QAbstractSocket::SocketError socketError);
void sessionOpened();
void getInfo();
void readData();
void connectToServer(QString ipAddr, quint32 port);
void disconnectFromServer();
private:
public:
Client(QString purpose);
};
#endif // CLIENT_H
server.h:
#ifndef SERVER_H
#define SERVER_H
#include <QTcpServer>
#include <iostream>
//#include <QtTest/QTest>
#include <QSignalSpy>
#include <QTcpSocket>
#include <QDebug>
//#include <QMessageBox>
#include <QNetworkInterface>
#include <typeinfo>
#include <QStringList>
//#include <QSignalSpy>
QT_BEGIN_NAMESPACE
class QTcpServer;
class QNetworkSession;
QT_END_NAMESPACE
class Server : public QObject
{
Q_OBJECT
public slots:
void sessionOpened();
void sendFortune(void);
void sendData(QPair<QString, QPair<QString, QVariant> > data);
void sendFile(QVariant fileName);
void disconnectServer(void);
void openNewConnection(void);
signals:
void gotNewConnection(QVariant);
private:
QString purpose;
QTcpServer *tcpServer;
QString ipAddr;
quint32 port;
QTcpSocket *clientConnection;
quint32 BlockSize;
bool firstTime;
QSignalSpy * m_pSignalSpy;
QStringList fortunes;
QNetworkSession *networkSession;
//QNetworkConfiguration config;
public:
Server(QString ipAddr, quint32 port, QString purpose = "");
};
#endif // SERVER_H
The problem lies with your QNetworkSession, you have declared it in scope. Meaning once you leave that function the QNetworkSession gets destroyed. A destroyed object cannot emit a signal. Maybe make it a member variable or construct it in your header.