如何在 Qt 中读取下一个数据包

Posted

技术标签:

【中文标题】如何在 Qt 中读取下一个数据包【英文标题】:How to read next packet in Qt 【发布时间】:2015-06-26 18:12:53 【问题描述】:

我的客户端设置如下: 客户端首先发送一个数据包,其中包含要从套接字读取的实际数据的长度。 然后客户端自己发送实际数据。 (我在实际发送数据之前预先设置了数据包长度)。

我想知道如何在 Qt Creator 中读取下一个数据包。我收到一条错误消息,提示找不到这样的插槽:MyThread::readPacket (int)。我做错了什么?

(如果我读取的字节数错误,请忽略该部分)

我的服务器端代码如下:

#include "thread.h"
#include <QDebug>
#include "DataSetProtos.pb.h"
MyThread::MyThread(qintptr ID, QObject *parent) :
QThread(parent)

    this->socketDescriptor = ID;


void MyThread::run()

// thread starts here
    qDebug() << " Thread started";

    socket = new QTcpSocket();

// set the ID
  if(!socket->setSocketDescriptor(this->socketDescriptor))

    // something's wrong, we just emit a signal
    emit error(socket->error());
    return;


// connect socket and signal
// note - Qt::DirectConnection is used because it's multithreaded
//        This makes the slot to be invoked immediately, when the signal is emitted.

connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()), Qt::DirectConnection);
connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected()));

// We'll have multiple clients, we want to know which is which
qDebug() << socketDescriptor << " Client connected";

// make this thread a loop,
// thread will stay alive so that signal/slot to function properly
// not dropped out in the middle when thread dies

exec();


void MyThread::readyRead()

// get the information
char* buffer = new char[SIZE];
int iResult= socket->read(buffer, SIZE);

connect(socket,SIGNAL(readyRead()), this,     SLOT(readPacket(iResult)), Qt::DirectConnection);


void MyThread::readPacket(int bufferLength)

char *buffer = new char[bufferLength];
int iResult = socket->read(buffer, bufferLength);
printf("\n Read 2 : %d", iResult);



void MyThread::Print(Visualization::PacketData packet)

    printf("\n\nPacket Name : %s", packet.name());
    printf("\n Packet length : %d ", packet.length());


void MyThread::disconnected()

qDebug() << socketDescriptor << " Disconnected";


    socket->deleteLater();
exit(0);

我的thread.h文件如下:

#ifndef THREAD_H
#define THREAD_H

#include <QThread>
#include <QTcpSocket>
#include <QDebug>
#include "DataSetProtos.pb.h"

#define SIZE 500
class MyThread : public QThread

    Q_OBJECT
public:
    explicit MyThread(qintptr ID, QObject *parent = 0);

void run();

signals:
void error(QTcpSocket::SocketError socketerror);

public slots:
void readyRead();
void readPacket( int bufferLength);
void disconnected();
void Print( Visualization::PacketData packet );

private:
QTcpSocket *socket;
qintptr socketDescriptor;
;

#endif // THREAD_H

【问题讨论】:

Qt Creator 是一个 IDE。框架是Qt。 This answer 有一个完整的例子来说明如何做到这一点,而不需要线程。 Another answer 实现了多线程服务器。 【参考方案1】:

正如 MrPickles 所指出的,你让自己很难受。但是,您无需一路更改数据格式。您只需要在不需要时停止尝试使用线程。 Qt 中几乎没有任何东西需要使用线程,除了繁重的数据处理或类似的东西。绝对不是基本的网络 I/O。

我有一个与消息长度前缀的 protobuf 通信的应用程序。以下是我的 onReadyRead 实现的要点,更改了一些细节并删除了不必要的内容:

void ProtobufMessageReceiver::onReadyRead()

    //Some maximum size of a message to protect from malformed
    //packets or bugs. This value should be above the size of the largest
    //protobuf message you ever expect to receive.
    static const uint32_t MAX_ALLOWED_SIZE = 1024 * 1024; //1MB

    uint32_t msg_size;
    while ( m_socket->bytesAvailable() >= sizeof(msg_size) )
    
        m_socket->peek(reinterpret_cast<char*>(&msg_size), sizeof(msg_size));

        //OK, we have read in a 32-bit message size, now verify that
        //it is valid before we try to read that many bytes on the socket

        if ( msg_size > MAX_ALLOWED_SIZE )
        
            //You might throw an exception here or do some other error
            //handling. Like most Qt code, though, this app doesn't
            //use exceptions, so we handle a malformed packet by just
            //closing the socket. In a real app you'd probably at least
            //want to do some logging here, as this should be a very
            //abnormal case and signifies a bug on the sending end.
            m_socket->close();
            break;
        

        //Now check to see if we have that many bytes available to read in.
        //If we do, we have at least one full message waiting to process, if not,
        //we'll process the message on the next call to onReadyRead.
        if ( m_socket->bytesAvailable() >= sizeof(msgSize) + msgSize )
        
            m_socket->read(reinterpret_cast<char*>(&msg_size), sizeof(msg_size));

            QScopedArrayPointer<char> buffer(new char[msgSize]);
            m_socket->read(&buffer[0], msgSize);
            processMessage(buffer, msgSize);
        
        else
        
            break;
        
    

processMessage 函数现在将始终收到一个完全有效的 protobuf 流,该流可以使用 parseFromArray 进行解码。 (在这个例子中,我们可能会使用 protobuf Union Type 在一个流中交错几种不同的类型)。请注意,因为您不能保证每个 protobuf 消息都会只调用一次 onReadyRead,所以在其中包含该 while 循环非常重要。无论您是使用 Qt 还是其他使用套接字的方式,情况都是如此,因为 TCP 只是一个字节流,并没有划分消息边界。毕竟,这就是我们必须首先做长度前缀的全部原因。

【讨论】:

【参考方案2】:

当我们在 QT 中使用 signalslot 时,我们需要注意参数。在connect 下方不起作用,因为您的signal 是在没有参数的情况下发出的,但您在SLOT 中将int 作为参数提供。

connect(socket,SIGNAL(readyRead()), this, SLOT(readPacket(iResult)), Qt::DirectConnection);

根据Qt Documentation

信号和槽机制是类型安全的: 信号必须与接收槽的签名相匹配。

您可以按如下方式连接信号和插槽:

connect(&socket, SIGNAL(readyRead()),this, SLOT(readData()));

您的readData 方法如下所示

void MyThread :: readData()
    
    QString readCurData = socket.readAll();
    qDebug() << readCurData;           

如果您像这样读取套接字数据,那么您也不需要从客户端发送字节数。希望这对你有用。

【讨论】:

我还是 Qt 新手。我需要读取指定数据包长度的下一个数据包。我该怎么做?有可能吗? @AshwinVenkataraman : 我更新了我的答案,请检查它是否适合你 另外,检查connect()的返回码,避免再次犯此错误。或者使用新的 Qt5 connect() 语法,它都是静态检查的(在编译时)。【参考方案3】:

你让自己很难受。使用 JSON 对象发送/解析数据包,稍后感谢我。

它们在 Qt 中得到很好的支持。据我所知,没有理由不使用它们。

不需要发送数据包的长度。您的协议应如下所示:

    客户端发送带有 ID(不是长度)和数据的 JSON 对象。 服务器捕获对象,服务器的就绪读取函数将 JSON 对象传递给句柄请求函数。 处理请求函数只不过是一个处理数据包 ID 的 switch 语句。 从 switch 语句调用 ID 的函数处理。 从 JSON 对象解析数据非常简单。 当服务器想向客户端发送一些东西时,反之亦然。

还要注意,保存数据包ID的枚举/结构/类必须与客户端和服务器相同。

【讨论】:

我使用的是 protobuf 而不是 JSON。

以上是关于如何在 Qt 中读取下一个数据包的主要内容,如果未能解决你的问题,请参考以下文章

qt中如何解包利用python 的struct.pack()函数打包的数据

R 如何实现更快读取数据 - 使用readr包

如何在 PIG 脚本中从数据包中读取数据

CFReadStreamRead 读取时如何区分数据包?

如何在linux平台上将控制台输出重定向到qt中的GUI

OpenCV imshow() 防止 Qt / python 崩溃