如何在Qt中读取下一个数据包
How to read next packet in Qt
我的客户端设置如下:客户端首先发送一个数据包,其中包含要从套接字读取的实际数据的长度。然后客户端自己发送实际数据。(在实际发送数据之前,我正在预挂数据包长度)。
我想知道如何在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("nnPacket 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中几乎没有任何东西需要使用线程,除了繁重的数据处理或类似的东西。绝对不是基本的网络I/O。
我有一个应用程序,它使用以消息长度为前缀的protobufs进行通信。以下是我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只是一个字节流,不会划分消息边界。毕竟,这就是我们必须首先做长度前缀的全部原因。
当我们在QT中使用signal
和slot
时,我们需要小心参数。以下connect
将不起作用,因为您的signal
是在没有参数的情况下发出的,但您在SLOT
中将int
作为参数。
connect(socket,SIGNAL(readyRead()), this, SLOT(readPacket(iResult)), Qt::DirectConnection);
根据Qt文件
信号和插槽机制是类型安全的:信号必须与接收时隙的签名相匹配。
您可以按如下方式连接信号和插槽:
connect(&socket, SIGNAL(readyRead()),this, SLOT(readData()));
你的readData
方法如下
void MyThread :: readData()
{
QString readCurData = socket.readAll();
qDebug() << readCurData;
}
若您像这样读取套接字数据,那个么您也不需要从客户端发送字节数。希望这对你有用。
你让自己很难过。使用JSON对象发送/解析数据包,稍后感谢我。
他们在Qt中得到了很好的支持。就我所见,没有理由不使用它们。
无需发送数据包的长度。您的协议应该如下:
- 客户端发送带有ID(而不是长度)和数据的JSON对象
- 服务器捕获对象,服务器的ready read函数将JSON对象传递给句柄请求函数
- 句柄请求函数不过是一个处理数据包ID的switch语句
- 处理从switch语句调用的ID的函数
- 解析JSON对象中的数据非常容易
- 反之亦然,当服务器想要向客户端发送内容时
还要注意,包含数据包ID的enum/struct/class必须与客户端和服务器相同。
- 重新定位图像时如何前进到下一个内存块
- 为什么C++对链表中的下一个节点使用指针,而像 C# 或 Java 这样的语言只使用类 Node 的名称?
- 全局变量 多读取器 一个写入器多线程安全?
- 如何使用C++读取另一个程序中的源代码输出
- 用于在 C++ 中使用 while 循环查找下一个素数的简单函数
- CIN 仅在输入非数字值时跳过下一个 CIN
- 查找最小的下一个更大的元素
- 转到基于范围的 for 循环中的下一个迭代器
- 如何检查流中文件的下一个单词是否为 alpha?
- 如何打印下一个字母直到 Z 并继续到 A?
- 在使用堆栈为下一个最大数字编写代码时面临 SIGSEGV(分段错误)
- 使用一个内存集数组和单个堆栈在 O(n) 中查找数组的下一个更大元素
- 有没有一种标准方法可以在C++中获取第 n 个"下一个"浮点值
- 解压缩串联的 zlib 流而不读取下一个字节
- 快速从文件中读取下一个字符串,而无需移动文件的位置
- C++:如何查看正在读取的文件的下一个字符以比较字符?
- 如何读取.cpp文件中的下一个字符
- 如何在Qt中读取下一个数据包
- 在二进制整数 (C++) 中读取后,fstream 是否移动到下一个位置
- Qt:从文件中读取下一个字符时出错