赞
踩
虽然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();
- }
大端序代码打印结果:
小端序代码打印结果:
由代码可知,大端序要稍微复杂一些,但是符合互联网传输数据的要求,小端序的实现要简单不少,所以如果是非正规场合,使用小端是一种简洁的方式。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。