Qt 实现网络数据报文大小端数据的收发
1.大小端数据简介
大小端(Endianness)是计算机体系结构的一个术语,它描述了多字节数据在内存中的存储顺序。以下是大小端的定义和它们的特点:
大端(Big-Endian)
在大端模式中,一个字的最高有效字节(MSB,即最左边的那位)存储在最低的内存地址处,而最低有效字节(LSB,即最右边的那位)存储在最高的内存地址处。
例如,假设有一个16位的数字 0x1234,其在内存中的存储顺序如下:
地址增加方向 -->
[ 12 ] [ 34 ]
其中 [12] 是高字节,存储在低地址处;[34] 是低字节,存储在高地址处。
小端(Little-Endian)
在小端模式中,情况正好相反:一个字的最低有效字节存储在最低的内存地址处,而最高有效字节存储在最高的内存地址处。
继续上面的例子,16位的数字 0x1234 在小端模式下的存储顺序如下:
地址增加方向 -->
[ 34 ] [ 12 ]
其中 [34] 是低字节,存储在低地址处;[12] 是高字节,存储在高地址处。
2.小端数据收发
假设我们现在有一个UDP头的数据结构如下所示。
//UDP协议头
typedef struct
{
quint16 type; //报文类型
quint16 num; //报文序号,取值范围为0~65535
quint32 len; //报文长度
quint16 srcAddr; //信源地址
quint16 dstAddr; //信宿地址
quint8 year; //发送时间 年份后两位 UTC时间
quint8 month;
quint8 day;
quint8 hour;
quint8 minute;
quint8 second;
}UdpHeader;
数据发送:组包。
QByteArray pack(const Protocol::UdpHeader &header)
{
QByteArray byte;
byte.append((char*)&(header.type),2);
byte.append((char*)&(header.num),2);
byte.append((char*)&(header.len),4);
byte.append((char*)&(header.srcAddr),2);
byte.append((char*)&(header.dstAddr),2);
QString format = "yy-MM-dd-hh-mm-ss";
QDateTime dateTime = QDateTime::currentDateTime();
dateTime.setTimeSpec(Qt::LocalTime);
QDateTime utcTime = dateTime.toUTC();
QString strUtcTime = utcTime.toString(format);
QStringList timeList = strUtcTime.split('-');
quint8 year = timeList.at(0).toInt();
quint8 month = timeList.at(1).toInt();
quint8 day = timeList.at(2).toInt();
quint8 hour = timeList.at(3).toInt();
quint8 minute = timeList.at(4).toInt();
quint8 sec = timeList.at(5).toInt();
byte.append(year);
byte.append(month);
byte.append(day);
byte.append(hour);
byte.append(minute);
byte.append(sec);
return byte;
}
数据接收:拆包
void unPack(const QByteArray &byte, Protocol::UdpHeader &header)
{
if(byte.size() != UDP_HEADER_LEN)
{
return;
}
memcpy(&header,byte.data(),sizeof(Protocol::UdpHeader));
}
3.大端数据收发
还是上面的头例子。
数据发送:组包。
使用QDataStream类作为辅助,设置setByteOrder为大端序列。
QByteArray packBigEndian(const Protocol::UdpHeader &header)
{
QByteArray byte;
QDataStream stream(&byte,QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::BigEndian);
stream<<(header.type);
stream<<(header.num);
stream<<(header.len);
stream<<(header.srcAddr);
stream<<(header.dstAddr);
QString format = "yy-MM-dd-hh-mm-ss";
QDateTime dateTime = QDateTime::currentDateTime();
dateTime.setTimeSpec(Qt::LocalTime);
QDateTime utcTime = dateTime.toUTC();
QString strUtcTime = utcTime.toString(format);
QStringList timeList = strUtcTime.split('-');
quint8 year = timeList.at(0).toInt();
quint8 month = timeList.at(1).toInt();
quint8 day = timeList.at(2).toInt();
quint8 hour = timeList.at(3).toInt();
quint8 minute = timeList.at(4).toInt();
quint8 sec = timeList.at(5).toInt();
stream<<year;
stream<<month;
stream<<day;
stream<<hour;
stream<<minute;
stream<<sec;
return byte;
}
数据接收:拆包。
对于多字节的数据,都需要单独使用qToBigEndian转换为大端。
void unPackBigEndian(const QByteArray &byte, Protocol::UdpHeader &header)
{
if(byte.size() != UDP_HEADER_LEN)
{
return;
}
memcpy(&header,byte.data(),sizeof(Protocol::UdpHeader));
header.type = qToBigEndian(header.type);
header.num= qToBigEndian(header.num);
header.len= qToBigEndian(header.len);
header.srcAddr= qToBigEndian(header.srcAddr);
header.dstAddr= qToBigEndian(header.dstAddr);
}