Qt中udp指令,大小端,帧头帧尾实际示例

前言

虽然QT中,udp发送和接收,其实非常简单,但是实际工作中,其实涉及到帧头帧尾,字节对齐,以及大小端序的问题。比如网络中,正规的一般都是大端序,而不是小端序,大多数的系统中,默认存储的都是小端序。 其次,udp指令,在代码中,我们一般都会设置结构体,如果是小端可以直接使用结果体转换,如果是大端序,需要先将数据转尾大端然后发送,接收端再将大端序数据转为小端进行分析和保存。以下是一个实际案例的测试demo:

注意:

以下协议和帧头等都是根据近期项目随机定的,不涉及保密。

指令接收端口为18000; 目的是开启视频保存。

发送端:

main.cpp

cpp 复制代码
#include <QUdpSocket>
#include <QtEndian>
#include <QDataStream>

// 以1字节对齐:
#pragma pack(1)
struct SaveVideoCtrlPara
{
    // 帧头 0xaa56
    unsigned short dataHead = 0xbb58;

    // 第一路
    quint8    video1_isSave       = 0x00;       // 0x00:close    0x01:start save
    quint8    video1_type         = 0x00;       // 0x00:null     0x01:rtsp        0x02:rtp
    quint32   video1_para         = 0x00;       // rtsp: IP      rtp :recv port
    quint32   video1_reserve      = 0x00;       // 预留
    char      video1_fileName[64] = "";         // 保存文件名

    // 第一路
    quint8    video2_isSave       = 0x00;       // 0x00:close    0x01:start save
    quint8    video2_type         = 0x00;       // 0x00:null     0x01:rtsp        0x02:rtp
    quint32   video2_para         = 0x00;       // rtsp: IP      rtp :recv port
    quint32   video2_reserve      = 0x00;       // 预留
    char      video2_fileName[64] = "";         // 保存文件名

    // 包尾
    // unsigned short dataTail = 0x1111;
};
#pragma pack(pop)

// 转为大端序
QByteArray serializeStruct(const SaveVideoCtrlPara &data) {
    QByteArray byteArray;
    QDataStream stream(&byteArray, QIODevice::WriteOnly);

    // 将数据转换为大端序
    stream << qToBigEndian(data.dataHead);
    stream << qToBigEndian(data.video1_isSave);
    stream << qToBigEndian(data.video1_type);
    stream << qToBigEndian(data.video1_para);
    stream << qToBigEndian(data.video1_reserve);
    stream << QString(data.video1_fileName).toUtf8();

    stream << qToBigEndian(data.video2_isSave);
    stream << qToBigEndian(data.video2_type);
    stream << qToBigEndian(data.video2_para);
    stream << qToBigEndian(data.video2_reserve);
    stream << QString(data.video2_fileName).toUtf8();

    return byteArray;
}

int main(int argc, char *argv[])
{

    QByteArray temp_byte_array;
    SaveVideoCtrlPara test;

    test.video1_type = 0x02;
    test.video1_para = 10011;
    strcpy(test.video1_fileName,"test1");
    test.video2_type = 0x02;
    test.video2_para = 10012;
    strcpy(test.video2_fileName,"test2");

    QUdpSocket *m_pUdpSocket = new QUdpSocket();
    m_pUdpSocket->bind(QHostAddress("127.0.0.1"),18001);
    
    // 小端序
    // QByteArray frame = QByteArray((char *)&test, sizeof(SaveVideoCtrlPara));
    
    // 大端序
    QByteArray frame = serializeStruct(test);
    
    // send
    m_pUdpSocket->writeDatagram(frame, QHostAddress("127.0.0.1"), 18000);

}

接收端:

main.cpp

cpp 复制代码
#include <QApplication>
#include <QUdpSocket>
#include <QDataStream>
#include <QtEndian>

// 以1字节对齐:
#pragma pack(1)
struct SaveVideoCtrlPara
{
    // 帧头 0xaa56
    unsigned short dataHead = 0xbb58;

    // 第一路
    quint8    video1_isSave       = 0x00;       // 0x00:close    0x01:start save
    quint8    video1_type         = 0x00;       // 0x00:null     0x01:rtsp        0x02:rtp
    quint32   video1_para         = 0x00;       // rtsp: IP      rtp :recv port
    quint32   video1_reserve      = 0x00;       // 预留
    char      video1_fileName[64] = "";         // 保存文件名

    // 第一路
    quint8    video2_isSave       = 0x00;       // 0x00:close    0x01:start save
    quint8    video2_type         = 0x00;       // 0x00:null     0x01:rtsp        0x02:rtp
    quint32   video2_para         = 0x00;       // rtsp: IP      rtp :recv port
    quint32   video2_reserve      = 0x00;       // 预留
    char      video2_fileName[64] = "";         // 保存文件名

    // 包尾
    // unsigned short dataTail = 0x1111;
};
#pragma pack(pop)

SaveVideoCtrlPara deserializeStruct(const QByteArray &byteArray)
{
    QDataStream stream(byteArray);
    QByteArray tmp_str1,tmp_str2;
    //tmp_str1.resize(64);
    //tmp_str2.resize(64);
    stream.setByteOrder(QDataStream::BigEndian); // 设置为大端序

    SaveVideoCtrlPara data;

    // 从数据流中读取并转换为小端序
    stream >> data.dataHead;
    stream >> data.video1_isSave;
    stream >> data.video1_type;
    stream >> data.video1_para;
    stream >> data.video1_reserve;
    stream >> tmp_str1;

    stream >> data.video2_isSave;
    stream >> data.video2_type;
    stream >> data.video2_para;
    stream >> data.video2_reserve;
    stream >> tmp_str2;

    data.dataHead = qFromBigEndian(data.dataHead);
    data.video1_isSave = qFromBigEndian(data.video1_isSave);
    data.video1_type = qFromBigEndian(data.video1_type);
    data.video1_para = qFromBigEndian(data.video1_para);
    data.video1_reserve = qFromBigEndian(data.video1_reserve);
    strcpy(data.video1_fileName, tmp_str1.toStdString().c_str());

    data.video2_isSave = qFromBigEndian(data.video2_isSave);
    data.video2_type = qFromBigEndian(data.video2_type);
    data.video2_para = qFromBigEndian(data.video2_para);
    data.video2_reserve = qFromBigEndian(data.video2_reserve);
    strcpy(data.video2_fileName, tmp_str2.toStdString().c_str());

    return data;
}


int main(int argc, char *argv[])
{

    QApplication app(argc, argv);
    QUdpSocket *m_pUdpSocket = new QUdpSocket();
    m_pUdpSocket->bind(QHostAddress("127.0.0.1"),18000);
    QObject::connect(m_pUdpSocket,&QUdpSocket::readyRead,[=](){
        QByteArray frame;
        while(m_pUdpSocket->hasPendingDatagrams())
        {
            frame.resize(m_pUdpSocket->pendingDatagramSize());
            // 接收数据报,将其存放到datagram中
            m_pUdpSocket->readDatagram(frame.data(), frame.size());

            // 大端序
            SaveVideoCtrlPara pFrame = deserializeStruct(frame);
            if(pFrame.dataHead == 0xaa56)
            {
                qDebug()<<"接收到信息*********";
                qDebug()<<"video1_isSave:"  <<pFrame.video1_isSave;
                qDebug()<<"video1_type  :"  <<pFrame.video1_type  ;
                if(pFrame.video1_type==0x01)
                    qDebug()<<"video1_para  :"  <<QHostAddress(pFrame.video1_para).toString();
                else
                    qDebug()<<"video1_para  :"  <<pFrame.video1_para;

                qDebug()<<"video1_fileName:"<<pFrame.video1_fileName;

                qDebug()<<"video2_isSave:"  <<pFrame.video2_isSave;
                qDebug()<<"video2_type  :"  <<pFrame.video2_type  ;
                if(pFrame.video2_type==0x01)
                    qDebug()<<"video2_para  :"  <<QHostAddress(pFrame.video2_para).toString();
                else
                    qDebug()<<"video2_para  :"  <<pFrame.video2_para;
                qDebug()<<"video2_fileName:"<<pFrame.video2_fileName;
                qDebug()<<"*****************";
            }

//            // 小端序
//            struct SaveVideoCtrlPara *pFrame = (struct SaveVideoCtrlPara *)frame.data();
//            if(pFrame->dataHead == 0xaa56)
//            {
//                qDebug()<<"接收到信息*********";
//                qDebug()<<"video1_isSave:"  <<pFrame->video1_isSave;
//                qDebug()<<"video1_type  :"  <<pFrame->video1_type  ;
//                if(pFrame->video1_type==0x01)
//                    qDebug()<<"video1_para  :"  <<QHostAddress(pFrame->video1_para).toString();
//                else
//                    qDebug()<<"video1_para  :"  <<pFrame->video1_para;
//                qDebug()<<"video1_fileName:"<<pFrame->video1_fileName;

//                qDebug()<<"video2_isSave:"  <<pFrame->video2_isSave;
//                qDebug()<<"video2_type  :"  <<pFrame->video2_type  ;
//                if(pFrame->video2_type==0x01)
//                    qDebug()<<"video2_para  :"  <<QHostAddress(pFrame->video2_para).toString();
//                else
//                    qDebug()<<"video2_para  :"  <<pFrame->video2_para;
//                qDebug()<<"video2_fileName:"<<pFrame->video2_fileName;
//                qDebug()<<"*****************";
//            }
        }
    });

    return app.exec();
}

结论

大端序代码打印结果:

小端序代码打印结果:

由代码可知,大端序要稍微复杂一些,但是符合互联网传输数据的要求,小端序的实现要简单不少,所以如果是非正规场合,使用小端是一种简洁的方式。

相关推荐
laimaxgg1 分钟前
五、central cache的设计
c++·缓存·性能优化
虾球xz1 小时前
游戏引擎学习第289天:将视觉表现与实体类型解耦
c++·学习·游戏引擎
hgdlip1 小时前
手机怎么查看网络ip地址?安卓/iOS设备查询指南
网络·tcp/ip·智能手机
liuzhangfeiabc1 小时前
[luogu12542] [APIO2025] 排列游戏 - 交互 - 博弈 - 分类讨论 - 构造
c++·算法·题解
黑塞1232 小时前
C++ Pimpl(Pointer to Implementation)设计思想
c++
byte轻骑兵2 小时前
【Bluedroid】蓝牙HID Device virtual_cable_unplug全流程源码解析
c++·hid·bluedroid
2302_799525742 小时前
【Linux】第二十四章 管理网络安全
linux·网络·web安全
四谷夕雨2 小时前
C++数据结构——红黑树
开发语言·数据结构·c++
小王努力学编程2 小时前
【数据库课程设计】网上投票管理系统
数据库·c++·qt·课程设计
oscar9992 小时前
1.3 C++之变量与数据类型
开发语言·c++