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

结论

大端序代码打印结果:

小端序代码打印结果:

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

相关推荐
wangjun51591 小时前
linux,物理机、虚拟机,同时内外网实现方案;物理机与虚拟机互通网络;
linux·服务器·网络
Bruce-li__1 小时前
深入理解Python asyncio:从入门到实战,掌握异步编程精髓
网络·数据库·python
self-discipline6341 小时前
【计网速通】计算机网络核心知识点与高频考点——数据链路层(二)
网络·网络协议·计算机网络
车载小杜1 小时前
基于指针的线程池
开发语言·c++
CryptoPP1 小时前
深入实践:基于WebSocket的全球化金融数据实时对接方案。 马来西亚、印度、美国金融数据API
websocket·网络协议·金融
叫醒你笛莎3 小时前
IGMP(Internet Group Management Protocol)与组播技术深度解析
网络
网络抓包与爬虫3 小时前
Wireshark——抓包分析
websocket·网络协议·tcp/ip·http·网络安全·https·udp
lulinhao3 小时前
HCIA/HCIP基础知识笔记汇总
网络·笔记
暴走的YH3 小时前
【网络协议】三次握手与四次挥手
网络·网络协议
yuzhangfeng3 小时前
【云计算物理网络】数据中心网络架构设计
网络·云计算