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

news/2024/7/8 7:40:55 标签: udp, 网络协议, 网络, qt, c++

前言

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

注意:

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

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

        

发送端:

main.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

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

结论

大端序代码打印结果:

小端序代码打印结果:

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


http://www.niftyadmin.cn/n/5536749.html

相关文章

密码学:对称加密算法、非对称加密算法、哈希算法

「作者简介」:冬奥会网络安全中国代表队,CSDN Top100,就职奇安信多年,以实战工作为基础著作 《网络安全自学教程》,适合基础薄弱的同学系统化的学习网络安全,用最短的时间掌握最核心的技术。 这一章节我们需要知道密码算法分哪几类,对称算法、非对称算法、哈希算法分别有…

【前端CSS3】CSS显示模式(黑马程序员)

文章目录 一、前言&#x1f680;&#x1f680;&#x1f680;二、CSS元素显示模式&#xff1a;☀️☀️☀️2.1 什么是元素显示模式2.2 块元素2.3 行内元素2.4 行块元素2.5 元素显示模式的转换 三、总结&#x1f680;&#x1f680;&#x1f680; 一、前言&#x1f680;&#x1f…

NLP入门——前馈词袋分类模型的搭建、训练与预测

模型的搭建 线性层 >>> import torch >>> from torch import nn >>> class DBG(nn.Module): ... def forward(self,x): ... print(x.size()) ... return x ... >>> tmod nn.Sequential(nn.Linear(3,4),DB…

CEPH 系统盘挂了,如何使用数据盘恢复

硬盘损坏是早晚的时&#xff0c;CEHP数据盘坏了&#xff0c;使用CEPH的基本都轻车熟路了&#xff0c;如果系统盘坏了呢&#xff1f;不知道的可能会采取整个系统盘全做的方式 前提条件&#xff1a;使用cephadm搭建集群 如果换服务器&#xff0c;请确保CEPH数据盘放到其它服务器上…

【CentOS 7 上安装 Oracle JDK 8u333】

文章目录 下载 Oracle JDK 8u333&#xff1a;上传 RPM 包到服务器安装 Oracle JDK设置 JAVA_HOME 环境变量验证 下载 Oracle JDK 8u333 访问 https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html 找到 JDK 8u333 版本&#xff0c;并下载适用于 L…

Spark性能优化(第22天)

一、Spark性能优化概述 二、Spark性能优化策略 三、理论分析 四、实践案例分析 五、监控与诊断 六、持续优化与改进 文章目录 引言一、Spark性能优化概述二、Spark性能优化策略1.1 开发调优2.1资源调优3.1 数据倾斜调优4.1 Shuffle调优 三、理论分析四、实践案例分析五、监控与…

SwiftUI中List的liststyle样式及使用详解添加、移动、删除、自定义滑动

SwiftUI中的List可是个好东西&#xff0c;它用于显示可滚动列表的视图容器&#xff0c;类似于UITableView。在List中可以显示静态或动态的数据&#xff0c;并支持垂直滚动。List是一个数据驱动的视图&#xff0c;当数据发生变化时&#xff0c;列表会自动更新。针对List&#xf…

location匹配和rewrite重定向

目录 location 匹配 location匹配的分类和优先级 优先级细分 实际网站中的使用规则 1.用精确匹配来实现网站的首页 访问网站的首页 &#xff08; /&#xff09; 2.用正则匹配来实现静态请求的页面和图片 匹配静态页面 访问图片或者指定的后缀名 3.用一般匹配转发.php…