QTcpSocket: reading and writing

前端 未结 2 655
广开言路
广开言路 2020-12-01 03:20

I know some similar questions may have been asked already, but the answers to those I found covered very specific problems and I still haven\'t figured it out.

In my

相关标签:
2条回答
  • 2020-12-01 03:48

    As you said, you need to wait that your header is entirely sent, before reading it, and then read the good number of bytes and emit a signal for data availability.

    Here an example (untested) :

    //header file
    
    class Peer {
    //[...]
    protected:
       bool m_headerRead; //initialize to false
       unsigned int m_size_of_data_to_read;
    //[...]
    };
    
    //source file
    void QPeer::sendData(QByteArray data)
    {
      int size = data.size();
      _socket->write((const char*) &size, sizeof(int);
      //use directly QIODevice::write(QByteArray)
      _socket->write(data);
    }
    
    void QPeer::readData()
    {
        int bytes = _socket->bytesAvailable();
        bool contains_enough_data = true;
    
        while (contains_enough_data) {
           if (! m_headerRead && _socket->bytesAvailable() >= sizeof(int)) {
             //read header only and update m_size_of_data_to_read
             m_headerRead = true;
            } else if (m_headerRead && _socket->bytesAvailable >= m_size_of_data_to_read) {
              //read data here
              m_headerRead = false;
              emit dataAvailable();
           } else {
               contains_enough_data = false; //wait that further data arrived
           }
        }
    }
    
    0 讨论(0)
  • 2020-12-01 04:04

    I worked on a project that does what you expect, see here the solution that I developed to our problems, simplified to be easier to understand:

    Edited, added support to the server deal with multiple clients.

    Client.h:

    #include <QtCore>
    #include <QtNetwork>
    
    class Client : public QObject
    {
        Q_OBJECT
    public:
        explicit Client(QObject *parent = 0);
    
    public slots:
        bool connectToHost(QString host);
        bool writeData(QByteArray data);
    
    private:
        QTcpSocket *socket;
    };
    

    Client.cpp:

    #include "client.h"
    
    static inline QByteArray IntToArray(qint32 source);
    
    Client::Client(QObject *parent) : QObject(parent)
    {
        socket = new QTcpSocket(this);
    }
    
    bool Client::connectToHost(QString host)
    {
        socket->connectToHost(host, 1024);
        return socket->waitForConnected();
    }
    
    bool Client::writeData(QByteArray data)
    {
        if(socket->state() == QAbstractSocket::ConnectedState)
        {
            socket->write(IntToArray(data.size())); //write size of data
            socket->write(data); //write the data itself
            return socket->waitForBytesWritten();
        }
        else
            return false;
    }
    
    QByteArray IntToArray(qint32 source) //Use qint32 to ensure that the number have 4 bytes
    {
        //Avoid use of cast, this is the Qt way to serialize objects
        QByteArray temp;
        QDataStream data(&temp, QIODevice::ReadWrite);
        data << source;
        return temp;
    }
    

    Server.h:

    #include <QtCore>
    #include <QtNetwork>
    
    class Server : public QObject
    {
        Q_OBJECT
    public:
        explicit Server(QObject *parent = 0);
    
    signals:
        void dataReceived(QByteArray);
    
    private slots:
        void newConnection();
        void disconnected();
        void readyRead();
    
    private:
        QTcpServer *server;
        QHash<QTcpSocket*, QByteArray*> buffers; //We need a buffer to store data until block has completely received
        QHash<QTcpSocket*, qint32*> sizes; //We need to store the size to verify if a block has received completely
    };
    

    Server.cpp:

    #include "server.h"
    
    static inline qint32 ArrayToInt(QByteArray source);
    
    Server::Server(QObject *parent) : QObject(parent)
    {
        server = new QTcpServer(this);
        connect(server, SIGNAL(newConnection()), SLOT(newConnection()));
        qDebug() << "Listening:" << server->listen(QHostAddress::Any, 1024);
    }
    
    void Server::newConnection()
    {
        while (server->hasPendingConnections())
        {
            QTcpSocket *socket = server->nextPendingConnection();
            connect(socket, SIGNAL(readyRead()), SLOT(readyRead()));
            connect(socket, SIGNAL(disconnected()), SLOT(disconnected()));
            QByteArray *buffer = new QByteArray();
            qint32 *s = new qint32(0);
            buffers.insert(socket, buffer);
            sizes.insert(socket, s);
        }
    }
    
    void Server::disconnected()
    {
        QTcpSocket *socket = static_cast<QTcpSocket*>(sender());
        QByteArray *buffer = buffers.value(socket);
        qint32 *s = sizes.value(socket);
        socket->deleteLater();
        delete buffer;
        delete s;
    }
    
    void Server::readyRead()
    {
        QTcpSocket *socket = static_cast<QTcpSocket*>(sender());
        QByteArray *buffer = buffers.value(socket);
        qint32 *s = sizes.value(socket);
        qint32 size = *s;
        while (socket->bytesAvailable() > 0)
        {
            buffer->append(socket->readAll());
            while ((size == 0 && buffer->size() >= 4) || (size > 0 && buffer->size() >= size)) //While can process data, process it
            {
                if (size == 0 && buffer->size() >= 4) //if size of data has received completely, then store it on our global variable
                {
                    size = ArrayToInt(buffer->mid(0, 4));
                    *s = size;
                    buffer->remove(0, 4);
                }
                if (size > 0 && buffer->size() >= size) // If data has received completely, then emit our SIGNAL with the data
                {
                    QByteArray data = buffer->mid(0, size);
                    buffer->remove(0, size);
                    size = 0;
                    *s = size;
                    emit dataReceived(data);
                }
            }
        }
    }
    
    qint32 ArrayToInt(QByteArray source)
    {
        qint32 temp;
        QDataStream data(&source, QIODevice::ReadWrite);
        data >> temp;
        return temp;
    }
    

    Note: Do not use this method to transfer large files because with this method the entire contents of the message is put inside the memory before sent and this causes a high memory usage. And because 32 bits signed INT has max value to 2,147,483,647, if your input data has a value higher than that in bytes it won't work. Take care.

    0 讨论(0)
提交回复
热议问题