0
votes

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 !

1
void DataSender::sendPeriodicData(void){ mTcpSocket-> ????eyllanesc
Do not use any waitFor methods. That's your problem. You don't need to flush or do any other magic. Write asynchronous code, and let the event loop run. Don't block. The world is asynchronous. The more you pretend it isn't, the more spaghetti the code becomes, and more bugs you'll have (IMHO).Kuba hasn't forgotten Monica
My bad @eyllanesc , it must be a ^C ^V typo.Clovel
Thanks for your advice @KubaOber , I’ll test that tonight. I will keep you postedClovel
Hi, sorry for the wait, I haven't worked on this project since I asked the question. @KubaOber orry to announce that removing the waitForBytesWritten does not solve the stalling issue.Clovel

1 Answers

1
votes

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