Qt实现udp发送和接收报文,有效性判断,解析报文数据,调试中如遇到通信失败,尝试关闭防火墙、关闭其他无关网卡,尤其是虚拟机网卡。
UdpCenter.h
#ifndef UDPCENTER_H
#define UDPCENTER_H
#include <QObject>
#include <QUdpSocket>
#include <QHostAddress>
/**
* @brief UDP通信报文固定报文头
*/
struct CMMI_CommonHeader{
unsigned char flag; //标识
unsigned short length;//大小
unsigned char serialNum;//序号
unsigned int time;//时间戳
};
class UdpCenter : public QObject
{
Q_OBJECT
public:
explicit UdpCenter(QObject *parent = 0);
void sendData(QByteArray data,QString ip);
void initSocket();//初始化UDP
void stopSocket();//断开UDP
public slots:
void readPendingDatagrams();
private:
void processTheDatagram(QByteArray recvBuff);
private:
QUdpSocket *m_pUdpSocketRece; //接收数据
QUdpSocket *m_pUdpSocketSend; //发送数据
};
#endif // UDPCENTER_H
UdpCenter.cpp
#include "udpcenter.h"
#include "rgglogal.h"
#include "protocol/protocol.h"
using namespace PROTOCOL;
UdpCenter::UdpCenter(QObject *parent) : QObject(parent)
{
m_pUdpSocketRece = nullptr;
m_pUdpSocketSend = nullptr;
}
/**
* @brief 发送报文
* @param QByteArray报文数据
*/
void UdpCenter::sendData(QByteArray data,QString ip)
{
if(m_pUdpSocketSend)
{
m_pUdpSocketSend->writeDatagram(data,QHostAddress(ip),21505);
}
}
/**
* @brief 初始化UDP套接字,本例中接收和发送分别定义了一个套接字
*/
void UdpCenter::initSocket()
{
//接收数据套接字
m_pUdpSocketRece = new QUdpSocket(this);
bool isOKm_pUdpSocketRece = m_pUdpSocketRece->bind(QHostAddress::AnyIPv4,21505);
qDebug()<<"["<<__FILE__<<"]"<<__LINE__<<__FUNCTION__<<"isOKm_pUdpSocketRece "<<isOKm_pUdpSocketRece;
m_pUdpSocketRece->joinMulticastGroup(QHostAddress("224.0.100.1"));//加入组播
//发送数据套接字
m_pUdpSocketSend = new QUdpSocket(this);
bool isOKm_pUdpSocketSend = m_pUdpSocketSend->bind(QHostAddress::AnyIPv4,21504,QUdpSocket::ShareAddress);
qDebug()<<"["<<__FILE__<<"]"<<__LINE__<<__FUNCTION__<<"isOKm_pUdpSocketSend "<<isOKm_pUdpSocketSend;
connect(m_pUdpSocketRece, SIGNAL(readyRead()),this, SLOT(readPendingDatagrams()));
}
/**
* @brief 断开UDP
*/
void UdpCenter::stopSocket()
{
m_pUdpSocketRece->disconnectFromHost();
m_pUdpSocketRece->close();
//m_pUdpSocketRece->abort();
m_pUdpSocketSend->disconnectFromHost();
m_pUdpSocketSend->close();
//m_pUdpSocketSend->abort();
m_pUdpSocketRece = nullptr;
m_pUdpSocketSend = nullptr;
}
/**
* @brief 对接收的网络数据进行有效性判断
*/
void UdpCenter::readPendingDatagrams()
{
QByteArray datagram;
while (m_pUdpSocketRece->hasPendingDatagrams()) {
datagram.resize(m_pUdpSocketRece->pendingDatagramSize());
QHostAddress sender;
quint16 senderPort;
m_pUdpSocketRece->readDatagram(datagram.data(), datagram.size(),&sender, &senderPort);
qDebug()<<"["<<__FILE__<<"]"<<__LINE__<<__FUNCTION__<<"recedatagram.size() "<<datagram.size();
processTheDatagram(datagram);
}
}
/**
* @brief 对接收的网络数据进行业务判断,处理数据
* @param recvBuff 网络数据
*/
void UdpCenter::processTheDatagram(QByteArray recvBuff)
{
if(recvBuff.size()<sizeof(CMMI_CommonHeader))
{
return ;
}
//解析报文头部
CMMI_CommonHeader t_tenpHead;
memset((char*)&t_tenpHead, 0,sizeof(CMMI_CommonHeader));
memcpy((char*)&t_tenpHead, recvBuff.data(), sizeof(CMMI_CommonHeader));
//根据报文头判断报文类型
switch(t_tenpHead.flag)
{
case FLAG_SAR_Heart://设备心跳报文
{
CMMI_Device_Heart *t_SARHeart = (CMMI_Device_Heart*)(recvBuff.data() + sizeof(CMMI_CommonHeader));//去掉头部,取后面120位
CGlogal::instance()->recvHeart(t_SARHeart);//后续处理报文
}break;
case FLAG_SAR_TgtInfo://目标信息报文
{
CMMI_Device_TgtInfo *t_Device_TgtInfo = {
0};
t_Device_TgtInfo = (CMMI_Device_TgtInfo*)(recvBuff.data()+sizeof(CMMI_CommonHeader));
CGlogal::instance()->recvDeviceTgtInfo(t_Device_TgtInfo);//后续处理报文
}break;
case FLAG_SAR_TgtInfo_Tend://目标库上报报文
{
CMMI_Tgtlib_Upload *t_Tgtlib_Upload = (CMMI_Tgtlib_Upload*)(recvBuff.data()+sizeof(CMMI_CommonHeader));
CGlogal::instance()->receTgtLibUpload(t_Tgtlib_Upload);//后续处理报文
}break;
}
}
发送报文调用示例
/**
* @param t_ip,目标IP
* @param t_array,数据,不包含头
* @param t_flag,报文标识
*/
void MainWindow::slotSendDisturbCtrl(QString t_ip, QByteArray t_array)
{
static unsigned int static_DisturbCtrlSerialNum = 0;//报文序号,递增
if(static_DisturbCtrlSerialNum == 256)
static_DisturbCtrlSerialNum = 0;
PROTOCOL::CMMI_CommonHeader t_Device_ParamSetHeader;//报文头
memset(&t_Device_ParamSetHeader,0,sizeof(PROTOCOL::CMMI_CommonHeader));//报文头初始化为0
t_Device_ParamSetHeader.flag = PROTOCOL::FLAG_Center_SARTgtCtrl;
t_Device_ParamSetHeader.time = this->getCurrTime();
t_Device_ParamSetHeader.serialNum = static_DisturbCtrlSerialNum++;
t_Device_ParamSetHeader.length = 8+t_array.size();
//发送报文,先添加报文头,再添加报文正文
QByteArray t_DisturbCtrlText;
t_DisturbCtrlText.append((char*)&t_Device_ParamSetHeader,8);
t_DisturbCtrlText.append(t_array);
m_myUDP->sendData(t_DisturbCtrlText,t_ip);//IP可以是组播地址,以广播形式发送,4个255代表发送给所有组播地址
//如果传进来的报文正文是结构体,需要转换为QByteArray:t_Tgtlib_Bind为结构体
//QByteArray t_array = QByteArray((char*)&t_Tgtlib_Bind,sizeof(PROTOCOL::CMMI_Tgtlib_Bind));
}