张家界市网站建设_网站建设公司_后端工程师_seo优化
2025/12/29 19:01:43 网站建设 项目流程

QDataStream 是 Qt 中用于序列化和反序列化二进制数据的类。它可以将各种数据类型(包括基本类型、字符串、容器等)转换为字节序列,以便存储或传输,并且可以重新构造这些数据。

主要特点:

  1. 支持多种数据类型:包括整型、浮点型、字符串、字节数组等。

  2. 自动处理字节序:可以设置字节序(大端或小端)。

  3. 版本控制:可以设置版本号以处理数据结构的演变。

  4. 简单易用:使用流操作符(<< 和 >>)进行读写。

在聊天程序中,我们可以使用 QDataStream 来构建和解析消息,从而避免手动拼接和解析字符串。

下面是一个简单的示例,展示如何使用 QDataStream 序列化和反序列化一个包含消息类型、发送者、接收者和内容的消息。

假设我们的消息结构如下:

  • 消息类型(quint8)

  • 发送者(QString)

  • 接收者(QString)

  • 内容(QString)

序列化(写入)示例:

cpp
QByteArray serializeMessage(quint8 type, const QString &sender, const QString &receiver, const QString &content) {QByteArray byteArray;QDataStream stream(&byteArray, QIODevice::WriteOnly);stream.setVersion(QDataStream::Qt_5_15); // 设置版本,确保兼容性// 写入数据stream << type << sender << receiver << content;return byteArray;
}

反序列化(读取)示例:

cpp
bool parseMessage(const QByteArray &byteArray, quint8 &type, QString &sender, QString &receiver, QString &content) {QDataStream stream(byteArray);stream.setVersion(QDataStream::Qt_5_15);// 读取数据stream >> type >> sender >> receiver >> content;// 检查是否读取成功return stream.status() == QDataStream::Ok;
}

在网络上传输时,我们可以在消息前面加上长度前缀,以便接收方知道消息的边界。例如,先发送4个字节表示消息长度,再发送消息内容。

发送方:

cpp
QByteArray message = serializeMessage(type, sender, receiver, content);
quint32 messageSize = message.size();// 将消息长度转换为网络字节序(大端)
QByteArray sizeArray;
QDataStream sizeStream(&sizeArray, QIODevice::WriteOnly);
sizeStream.setVersion(QDataStream::Qt_5_15);
sizeStream << messageSize;// 先发送长度,再发送消息
socket->write(sizeArray);
socket->write(message);

接收方:
需要先读取4个字节得到消息长度,然后根据长度读取消息内容。

注意:QDataStream 在写入字符串时,会在字符串前面加上长度(quint32),所以整个消息的长度并不是简单的字符串长度之和。

在服务器端(C语言)解析这种格式可能会比较麻烦,因为需要按照QDataStream的格式来解析。如果服务器是C语言写的,可能需要考虑使用更简单的格式,或者使用相同的解析方式(C语言解析QDataStream格式)。

但是,如果服务器也是用Qt写的,那么使用QDataStream就会非常方便。

考虑到您的服务器是用C语言写的,我建议使用一种更简单的格式,例如:

  • 消息类型(1字节)

  • 发送者账号(固定8字节)

  • 接收者账号(固定8字节)

  • 消息内容(可变长度,前面加上4字节长度)

这样,服务器端解析起来会简单很多。

不过,既然您已经用C语言实现了服务器,并且之前的协议是定长的账号(8字节)和可变的消息内容,那么我们可以继续使用这种格式,只是使用QDataStream来帮助构建和解析消息。

对于文本消息,我们可以这样定义:

  • 消息类型:1字节('1')

  • 发送者账号:8字节(固定长度,不足补空格)

  • 接收者账号:8字节(固定长度,不足补空格)

  • 文本内容:可变长度,前面加上4字节长度(使用QDataStream写入)

使用QDataStream构建文本消息:

cpp
QByteArray buildTextMessage(const QString &sender, const QString &receiver, const QString &text) {QByteArray byteArray;QDataStream stream(&byteArray, QIODevice::WriteOnly);stream.setVersion(QDataStream::Qt_5_15);// 消息类型char msgType = '1';stream << msgType;// 发送者和接收者账号(固定8字节)QByteArray senderBytes = sender.toLatin1().leftJustified(8, ' ', true);QByteArray receiverBytes = receiver.toLatin1().leftJustified(8, ' ', true);stream.writeRawData(senderBytes.constData(), 8);stream.writeRawData(receiverBytes.constData(), 8);// 文本内容(带长度前缀)stream << text;return byteArray;
}

注意:使用QDataStream写入字符串(stream << text)时,会先写入一个quint32表示字符串长度,然后是字符串内容。这样,接收方就可以先读取长度,然后读取内容。

但是,这样构建的消息格式与之前协议中定义的格式(文本内容直接跟在接收者账号后面,没有长度前缀)不一致。因此,我们需要调整协议,为文本内容也加上长度前缀,这样解析起来会更方便。

调整后的协议:

  • 文本消息:类型(1)+发送者(8)+接收者(8)+文本内容长度(4)+文本内容

  • 文件信息:类型(2)+发送者(8)+接收者(8)+文件名长度(2)+文件名+文件大小(8)

  • 文件数据:类型(3)+发送者(8)+接收者(8)+块序号(4)+数据大小(4)+文件数据

  • 文件结束:类型(4)+发送者(8)+接收者(8)

这样,每个部分都有明确的长度,解析起来就方便多了。

对于文本消息,使用QDataStream构建:

cpp
QByteArray buildTextMessage(const QString &sender, const QString &receiver, const QString &text) {QByteArray byteArray;QDataStream stream(&byteArray, QIODevice::WriteOnly);stream.setVersion(QDataStream::Qt_5_15);// 消息类型quint8 msgType = 1; // 文本消息类型为1stream << msgType;// 发送者和接收者账号(固定8字节)QByteArray senderBytes = sender.toLatin1().leftJustified(8, ' ', true);QByteArray receiverBytes = receiver.toLatin1().leftJustified(8, ' ', true);stream.writeRawData(senderBytes.constData(), 8);stream.writeRawData(receiverBytes.constData(), 8);// 文本内容(UTF-8编码)QByteArray textBytes = text.toUtf8();quint32 textLength = textBytes.size();stream << textLength;stream.writeRawData(textBytes.constData(), textLength);return byteArray;
}

解析时,使用QDataStream按照相同的顺序读取即可。

但是,请注意,使用QDataStream写入整数(如textLength)时,默认使用大端字节序(网络字节序)。所以,在C语言服务器端解析时,需要将4字节长度从网络字节序转换为主机字节序。

同样,在C语言服务器端构建消息时,也需要将整数转换为网络字节序。

考虑到服务器端是C语言,我们可以在客户端使用QDataStream,并确保使用大端字节序(网络字节序)。QDataStream默认就是大端字节序,但我们可以显式设置:

cpp
stream.setByteOrder(QDataStream::BigEndian);

这样,客户端和服务器端就可以使用相同的字节序来解析整数了。

总结:

  1. 使用QDataStream可以简化客户端的消息构建和解析。

  2. 调整协议,为可变长度的字段加上长度前缀。

  3. 设置QDataStream为网络字节序(大端),以便与C语言服务器交互。

由于时间关系,这里只给出文本消息的构建和解析示例,文件消息类似。

希望这能帮助您改进网络传输方式。

 

QDataStream 详细介绍

一、QDataStream 是什么?

QDataStream 是 Qt 框架提供的用于二进制数据序列化和反序列化的类。它提供了一种简单、类型安全的方式来读写二进制数据,自动处理平台差异(如字节序、数据类型大小等)。

二、主要特点

1. 类型安全

cpp
QDataStream stream(&data, QIODevice::WriteOnly);
stream << QString("Hello") << qint32(100) << qreal(3.14);
// 自动知道每种类型的大小和格式

2. 自动字节序处理

cpp
stream.setByteOrder(QDataStream::LittleEndian);   // 小端
stream.setByteOrder(QDataStream::BigEndian);      // 大端(网络字节序,默认)

3. 版本控制

cpp
stream.setVersion(QDataStream::Qt_5_15);  // 设置序列化版本
// 不同版本的Qt可能有不同的序列化格式

4. 支持多种数据类型

  • 基本类型:qint8quint16qint32qint64floatdoublebool

  • Qt类型:QStringQByteArrayQListQMapQVariantQDateTimeQPixmapQImage

三、基本用法

1. 序列化(写入)

cpp
// 创建字节数组
QByteArray data;
QDataStream out(&data, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_15);  // 设置版本// 写入数据
out << QString("张三")       // 自动写入字符串长度+内容<< qint32(25)            // 4字节整数<< QDateTime::currentDateTime();  // 日期时间// 现在data包含了序列化的二进制数据

2. 反序列化(读取)

cpp
// 从字节数组读取
QDataStream in(data);
in.setVersion(QDataStream::Qt_5_15);  // 必须与写入时相同QString name;
qint32 age;
QDateTime datetime;// 按相同顺序读取
in >> name >> age >> datetime;if (in.status() != QDataStream::Ok) {qDebug() << "读取数据失败";
}

四、在网络传输中的应用

消息结构设计

cpp
// 消息头
struct MessageHeader {quint32 magicNumber = 0x12345678;  // 魔数,验证消息有效性quint32 totalSize = 0;             // 消息总大小quint8 messageType = 0;            // 消息类型quint32 sequenceId = 0;            // 序列号(可选)
};// 使用QDataStream序列化
QByteArray serializeMessage(MessageType type, const QByteArray &payload)
{MessageHeader header;header.messageType = static_cast<quint8>(type);header.totalSize = sizeof(MessageHeader) + payload.size();QByteArray data;QDataStream stream(&data, QIODevice::WriteOnly);stream.setVersion(QDataStream::Qt_5_15);// 写入消息头stream << header.magicNumber<< header.totalSize<< header.messageType<< header.sequenceId;// 写入消息体stream.writeRawData(payload.constData(), payload.size());return data;
}

消息解析

cpp
MessageType parseMessage(const QByteArray &data, QByteArray &payload)
{if (data.size() < sizeof(MessageHeader)) {return MessageType::Invalid;}QDataStream stream(data);stream.setVersion(QDataStream::Qt_5_15);MessageHeader header;stream >> header.magicNumber>> header.totalSize>> header.messageType>> header.sequenceId;// 验证魔数if (header.magicNumber != 0x12345678) {return MessageType::Invalid;}// 验证消息长度if (header.totalSize > data.size()) {return MessageType::Invalid;  // 数据不完整}// 读取消息体int payloadSize = header.totalSize - sizeof(MessageHeader);payload.resize(payloadSize);stream.readRawData(payload.data(), payloadSize);return static_cast<MessageType>(header.messageType);
}

五、QDataStream vs QByteArray vs JSON

 
 
特性 QDataStream QByteArray (手动) JSON
类型安全 ✅ 自动 ❌ 手动处理 ✅ 有类型
字节序处理 ✅ 自动 ❌ 手动处理 ✅ 无此问题
平台兼容性 ✅ 优秀 ❌ 差 ✅ 优秀
性能 ✅ 高 ✅ 高 ❌ 较低
可读性 ❌ 二进制 ❌ 二进制 ✅ 文本
数据大小 ✅ 紧凑 ✅ 紧凑 ❌ 较大
扩展性 ✅ 好(版本控制) ❌ 差 ✅ 好

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询