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();
}

结论

大端序代码打印结果:

小端序代码打印结果:

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

相关推荐
van叶~1 分钟前
算法妙妙屋-------1.递归的深邃回响:二叉树的奇妙剪枝
c++·算法
knighthood200112 分钟前
解决:ros进行gazebo仿真,rviz没有显示传感器数据
c++·ubuntu·ros
龙哥说跨境23 分钟前
如何利用指纹浏览器爬虫绕过Cloudflare的防护?
服务器·网络·python·网络爬虫
懒大王就是我37 分钟前
C语言网络编程 -- TCP/iP协议
c语言·网络·tcp/ip
半盏茶香41 分钟前
【C语言】分支和循环详解(下)猜数字游戏
c语言·开发语言·c++·算法·游戏
小堇不是码农1 小时前
在VScode中配置C_C++环境
c语言·c++·vscode
Jack黄从零学c++1 小时前
C++ 的异常处理详解
c++·经验分享
Elaine2023911 小时前
06 网络编程基础
java·网络
海绵波波1072 小时前
Webserver(4.3)TCP通信实现
服务器·网络·tcp/ip
热爱跑步的恒川5 小时前
【论文复现】基于图卷积网络的轻量化推荐模型
网络·人工智能·开源·aigc·ai编程