用qt写一个 UDP 收发的复杂结构体的自定义报文协议,包括报文头、报文体和crc,发送位置、速度角度等信息,给出一个具体实例和说明
时间: 2023-12-03 11:43:31 浏览: 390
QT UDP通信简单实例
好的,我来给你讲解一下。
首先,我们需要定义报文的结构体,包括报文头、报文体和CRC。这里我定义了一个名为udpPacket的结构体,代码如下:
```c++
struct udpPacket {
uint16_t header; // 报文头
uint16_t length; // 报文长度
uint32_t timestamp; // 时间戳
float x; // 发送位置x坐标
float y; // 发送位置y坐标
float z; // 发送位置z坐标
float speed; // 速度
float angle; // 角度
uint16_t crc; // CRC校验码
};
```
其中,报文头和报文长度分别使用2个字节的无符号整型表示,时间戳使用4个字节的无符号整型表示,发送位置、速度和角度均使用单精度浮点数表示,CRC校验码使用2个字节的无符号整型表示。
接下来,我们需要进行报文的序列化和反序列化。这里我使用了Qt的QByteArray类来进行序列化和反序列化,代码如下:
```c++
QByteArray serialize(const udpPacket& packet) {
QByteArray data;
QDataStream stream(&data, QIODevice::WriteOnly);
stream << packet.header << packet.length << packet.timestamp
<< packet.x << packet.y << packet.z
<< packet.speed << packet.angle << packet.crc;
return data;
}
udpPacket deserialize(const QByteArray& data) {
udpPacket packet;
QDataStream stream(data);
stream >> packet.header >> packet.length >> packet.timestamp
>> packet.x >> packet.y >> packet.z
>> packet.speed >> packet.angle >> packet.crc;
return packet;
}
```
在序列化时,我们使用QDataStream类将结构体中的各个成员依次写入QByteArray中;在反序列化时,我们则将QByteArray中的数据依次读取到结构体中。
最后,我们需要计算CRC校验码。这里我使用了一个名为crc16的函数来计算CRC校验码,代码如下:
```c++
uint16_t crc16(const QByteArray& data) {
uint16_t crc = 0xFFFF;
for (int i = 0; i < data.size(); i++) {
crc ^= static_cast<uint8_t>(data.at(i));
for (int j = 0; j < 8; j++) {
if (crc & 0x0001) {
crc = (crc >> 1) ^ 0xA001;
} else {
crc = crc >> 1;
}
}
}
return crc;
}
```
该函数使用了CRC-16-CCITT算法来计算CRC校验码。具体实现过程为:首先将CRC初始值设为0xFFFF,然后依次将数据中的每个字节和CRC进行异或操作,接着对每个字节进行8次移位和异或操作,最后得到的CRC值即为校验码。
综上所述,我们可以使用以上代码来实现一个UDP收发复杂结构体的自定义报文协议。下面是一个具体实例和说明:
我们假设我们需要发送一个名为“PositionInfo”的结构体,该结构体包含了位置、速度和角度等信息。我们可以定义如下:
```c++
struct PositionInfo {
float x;
float y;
float z;
float speed;
float angle;
};
```
接着,我们可以定义一个名为“udpPacket”的结构体,该结构体包含了报文头、报文体和CRC等信息。具体定义方式见上文。
然后,我们可以使用以上代码来对“udpPacket”进行序列化和反序列化,以及计算CRC校验码。
发送方的代码如下:
```c++
PositionInfo info;
// 填充info的各项数据
udpPacket packet;
packet.header = 0x1234;
packet.length = sizeof(udpPacket);
packet.timestamp = QDateTime::currentDateTime().toTime_t();
packet.x = info.x;
packet.y = info.y;
packet.z = info.z;
packet.speed = info.speed;
packet.angle = info.angle;
packet.crc = crc16(serialize(packet));
QUdpSocket socket;
socket.writeDatagram(serialize(packet), QHostAddress::Broadcast, 1234);
```
这里我们首先创建了一个名为“info”的“PositionInfo”结构体,并填充了各项数据。接着,我们创建了一个名为“packet”的“udpPacket”结构体,依次将报文头、报文体和CRC等信息填充到其中。最后,我们使用Qt的QUdpSocket类将序列化后的数据通过UDP协议发送出去。
接收方的代码如下:
```c++
QUdpSocket socket;
socket.bind(1234);
while (true) {
while (socket.hasPendingDatagrams()) {
QByteArray data;
data.resize(socket.pendingDatagramSize());
socket.readDatagram(data.data(), data.size());
udpPacket packet = deserialize(data);
if (packet.crc == crc16(data)) {
PositionInfo info;
info.x = packet.x;
info.y = packet.y;
info.z = packet.z;
info.speed = packet.speed;
info.angle = packet.angle;
// 处理接收到的数据
}
}
}
```
这里我们首先创建了一个名为“socket”的QUdpSocket对象,并绑定到了端口1234。然后,我们使用一个while循环来不断接收UDP数据包。当有数据包到达时,我们首先将其读取到一个QByteArray对象中,并使用deserialize函数将其反序列化到一个名为“packet”的“udpPacket”结构体中。接着,我们使用crc16函数计算数据包的CRC校验码,并与“packet”结构体中的CRC值进行比较。如果两者相等,则说明数据包没有被篡改,我们可以将“packet”中的各项数据填充到一个名为“info”的“PositionInfo”结构体中,并进行后续处理。
以上就是一个UDP收发复杂结构体的自定义报文协议的实现方法及具体实例。
阅读全文