QTcpSocket:读写 [英] QTcpSocket: reading and writing

查看:495
本文介绍了QTcpSocket:读写的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道一些类似的问题可能已经被问过,但是我发现的答案涵盖了非常具体的问题,我还是没有想出来。

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.

在我的程序中,我创建一个QObject(称为QPeer),使用QTcpSocket通过网络与另一个这样的对象进行通信。 QPeer有一个槽,接受一个QByteArray与数据( sendData(QByteArray))。该数组的全部内容被认为是一个消息,并且它们被写入套接字。我想做以下:每次一个消息被写,我想接收QPeer发出其信号 dataReceived(QByteArray)恰好一次,QByteArray包含整个消息。 (注意:所有的信号/时隙,连接QPeer与其套接字的私有连接和诸如 sendData(QByteArray)等公共连接使用

In my program I'm creating a QObject (called QPeer) that uses a QTcpSocket to communicate with another such object over a network. QPeer has a slot that accepts a QByteArray with data (sendData(QByteArray)). The entire contents of that array are considered to be one 'message' and they are written to the socket. I want to do the following: every time a message is written, I want the receiving QPeer to emit its signal dataReceived(QByteArray) exactly once, that QByteArray containing the entire message. (NOTE: all signals/slots, both private ones connecting the QPeer with its socket and the public ones such as sendData(QByteArray) are serialized by using Qt::QueuedConnection whenever necessary.)

我使用信号 QTcpSocket :: readyRead()用于异步读取套接字。现在我知道我不能只在sendData中调用 QTcpSocket :: write()一次,然后假设对于每个写我,QTcpSocket另一方产生正好一个readyRead信号。那么我该怎么办?

I use the signal QTcpSocket::readyRead() for asynchronous reading from the socket. Now I know I can't just call QTcpSocket::write() once in sendData and then assume that for every write I do, the QTcpSocket on the other side produces exactly one readyRead signal. So what should I do?

这是我的想法,请告诉我这是否可以工作:

This is my idea, please tell me if this will work:

void QPeer::sendData(QByteArray data)
{
    // TODO: write data.size() as raw int of exactly 4 bytes to socket
    const char *bytes = data.constData();
    int bytesWritten = 0;
    while (bytesWritten < data.size())
        bytesWritten += _socket->write(bytes + bytesWritten);
}

读取:

现在我想要读取函数(连接到 QTcpSocket :: readyRead())使用头(4字节int指定消息的长度),然后读取字节数; next emit dataReceived和这些字节。我有严重的麻烦,试图这样做。例如:如果readyRead被释放,我该怎么办,我可以读取消息的头,但不能指定字节数量?或者如果标头只是部分收到了?

now I want the read function (connected to QTcpSocket::readyRead()) to use the header (the 4 byte int specifying the length of the message) and then read that amount of bytes; next emit dataReceived with exactly those bytes. I'm having serious trouble trying to do this. For example: what to do if readyRead is emitted and I can read the header of a message, but not the amount of bytes specified? Or what if a header has only been received partially?

1。如何正确地将标题(4字节int)写入套接字?

2。如何正确实现读取函数,以便它能做我想要的?

欢迎任何提示。谢谢!

推荐答案

我在一个项目上做了你所期望的,看到我为我们的问题开发的解决方案,简化更容易理解:

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:

已编辑,为服务器添加了对多个客户端的支持

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:

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:

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

注意:不要使用此方法转移大文件,因为使用这种方法,消息的整个内容在发送之前被放在内存中,这会导致高内存使用。由于32位有符号INT具有最大值,因此 2,147,483,647 ,如果您的输入数据的值高于以字节为单位的值不工作。小心。

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.

这篇关于QTcpSocket:读写的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆