Qt Udp客户端

CUdpClient类实现了UDP客户端的功能,包括同步和异步发送数据,数据包的打包、解包和合法性检查,以及与服务器的交互逻辑。类中使用了QT的QUdpSocket进行网络通信,并有信号量机制处理同步反馈。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

头文件

#pragma once
#include <QtNetwork>
#include <QObject>
#include <iostream>   
#include <list> 
#include "Include/DataDef.h"
#pragma execution_character_set("utf-8")
using namespace std;
class CUdpClient : public QObject
{
	Q_OBJECT
public:
	CUdpClient();
	~CUdpClient();

	void Init();
	//同步发送
	int SynSendData(ST_TRANS_MSG stMsg,int iTimeout);
	//同步接收
	int  GetSyncMsgData(ST_TRANS_MSG &stOutData);
	//异步发送
	bool AsyncSendData(ST_TRANS_MSG stMsg);
	void GetNetAddress(ST_COMM_INFO &stCommInfo);
	int  SetNetAddress(ST_COMM_INFO stCommInfo);


	void CloseComm();
	bool BindListenPort();

	bool SendCommand(const unsigned char * pszCmd, int iCmdLength, int iTimeout,bool bIsResend = false);
	bool ReSendSyscData(const unsigned char * pData, int iDataLength);
	bool SendCommandEx(const unsigned char * pszCmd, int iCmdLength);
	bool ReSendSyscDataEx(const unsigned char * pData, int iDataLength);
	void GetLastCommandData(ST_TRANS_MSG &stlastData);
	
public:
	//组包
	virtual void Package(const unsigned char *pszSrcCmd, int iSrcCmdLen
		                   , unsigned char *pszDestCmd, int *piDestLen);
	//解包
	virtual void UnPackage(const unsigned char*pszSrcPackage, int iSrcPackageLength,
		            unsigned char *pszValidData, int *piValidDataLength);
	//检查报文的合法性
	virtual bool CheckMsgHeadAndEnd(const char * pDataBuf, int iBufLen);

	virtual bool JudgeIsValidityProtocal(const char * pDataBuf, int iBufLen);

	//处理来自服务器的控制命令
	virtual void ProServerCmd(RECV_DATA_TYPE, char cCmd);
	virtual RECV_DATA_TYPE GetMsgType(char cMsgType);
	//分拣处理数据
	void SortProData(ST_TRANS_MSG stMsg);
	//时间同步
	void SysTimeWithServer(const  char * pDataBuf, int iBufLen);
private:
	void PushRecvData(ST_TRANS_MSG stRecvData);
	void PopRecvData(ST_TRANS_MSG &stOutData);
	void ClearList();

public slots:
	void ProRecvData_Slot();

public:
	int m_iSyncCallTimeout;	//记录当前同步调用的timeout值
private:
	int m_iServerPort;   //服务器接收端口号
	int m_iLocalProt;//本地接收端口号
	QString m_strServerIp; //服务器IP
	QString m_strRecvBuf;//接收到的数据
	QUdpSocket *m_pUdpSocket;

	int m_iRetryCounter;//重试次数
	ST_TRANS_MSG m_stLastSendData;//上次同步发送的数据
	ST_TRANS_MSG m_stLastAsyncSendData;//上次同步发送的数据
	list<ST_TRANS_MSG> m_listSyncMsgData;
	ST_TRANS_MSG m_SyncMsgDataBuf;
	QMutex m_RecvDataMutex;
	QMutex m_UdpMutex;    //通信互斥锁

	QSemaphore m_RecvDataSem;

	bool m_bIsHaveSendData;//表示是否处于同步发送等待接收数据状态
};

源文件

#include "UdpClient.h"
#include <QMessageBox>
#include "TransLogWid/LogNotify.h"
#include "PublicFun/PublicFun.h"

extern CLogOperEx * g_StatusMonitorLog;

CUdpClient::CUdpClient()
{
	Init();
}


CUdpClient::~CUdpClient()
{
	if (NULL != m_pUdpSocket)
	{
		delete m_pUdpSocket;
		m_pUdpSocket = NULL;
	}
}

void CUdpClient::Init()
{	
	m_bIsHaveSendData = false;
	m_pUdpSocket = NULL;
	m_iServerPort =3000;
	m_iLocalProt = 3001;
	m_strServerIp = CPublicFun::getHostIpAddress();
	//m_strServerIp = "192.168.1.100";
	//创建Socket对象
	m_pUdpSocket = new QUdpSocket(this);
	connect(m_pUdpSocket, SIGNAL(readyRead()), this, SLOT(ProRecvData_Slot()));
	//绑定本地监控端口
	BindListenPort();
}



bool CUdpClient::BindListenPort()
{
	bool result = m_pUdpSocket->bind(m_iLocalProt);
	if (!result)
	{
		g_StatusMonitorLog->LogError("[%s][%s][%d] 绑定本地端口:%d 失败 ", __FILE__, __FUNCTION__, __LINE__,m_iLocalProt);
		return false;
	}	
	QString strShow("Bind Listen Port: ");
	strShow.append(QString::number(m_iLocalProt));
	
	CLogNotify::GetInstance().ShowMsgToHeartWid(strShow);
	g_StatusMonitorLog->LogError("[%s][%s][%d] %s ", __FILE__, __FUNCTION__, __LINE__, strShow.toStdString().c_str());
	return result;
}

bool CUdpClient::SendCommand(const unsigned char * pszCmd, int iCmdLength, int iTimeout, bool bIsResend)
{
	if (!bIsResend)
	{
		//打包
		Package(pszCmd, iCmdLength, (unsigned char*)m_stLastSendData.szData, &m_stLastSendData.iDataLen);
		CPublicFun::ShowDataToLog("ST", (unsigned char*)m_stLastSendData.szData, m_stLastSendData.iDataLen);
		//发送数据
		int iLen = m_pUdpSocket->writeDatagram((char*)m_stLastSendData.szData, m_stLastSendData.iDataLen, QHostAddress(m_strServerIp), m_iServerPort);
		if (iLen != m_stLastSendData.iDataLen)
		{
			g_StatusMonitorLog->LogError("[%s][%s][%d] WriteDatagram failed !", __FILE__, __FUNCTION__, __LINE__);
			return false;
		}
		m_stLastSendData.iMsgType = EN_MSG_TYPE_SEND;
		CLogNotify::GetInstance().ShowMsgToDataWid(m_stLastSendData);
	}
	else
	{

		QString strShow = QString("server没有反馈数据, 进行第 %1 次重发 ").arg(m_iRetryCounter);
		CLogNotify::GetInstance().NotifyMsgToDataWid(strShow);
		g_StatusMonitorLog->LogFatal("[%s][%s][%d] %s", __FILE__, __FUNCTION__, __LINE__, strShow.toStdString().c_str());
		
		m_stLastSendData.iMsgType = EN_MSG_TYPE_SEND;
		CLogNotify::GetInstance().ShowMsgToDataWid(m_stLastSendData);
		ReSendSyscData((unsigned char*)m_stLastSendData.szData, m_stLastSendData.iDataLen);
	}

	int iSemResult;
	while ((iSemResult = m_RecvDataSem.tryAcquire(1, iTimeout)) == false && errno == EINTR) continue;	/*!<超时等待同步反馈信号量释放>*/

	return iSemResult;
}

bool CUdpClient::SendCommandEx(const unsigned char * pszCmd, int iCmdLength)
{
	return ReSendSyscDataEx(pszCmd, iCmdLength);
}

bool CUdpClient::ReSendSyscData(const unsigned char * pData, int iDataLength)
{
	//发送数据
	int iLen = m_pUdpSocket->writeDatagram((char*)m_stLastSendData.szData, m_stLastSendData.iDataLen, QHostAddress(m_strServerIp), m_iServerPort);
	if (iLen != m_stLastSendData.iDataLen)
	{
		g_StatusMonitorLog->LogError("[%s][%s][%d] WriteDatagram failed !", __FILE__, __FUNCTION__, __LINE__);
		return false;
	}
	return true;
}

bool CUdpClient::ReSendSyscDataEx(const unsigned char * pData, int iDataLength)
{
	m_bIsHaveSendData = true;
	bool bRet = true;
	//发送数据
	int iLen = m_pUdpSocket->writeDatagram((char*)m_stLastSendData.szData, m_stLastSendData.iDataLen, QHostAddress(m_strServerIp), m_iServerPort);
	
	int iSemResult;
	while ((iSemResult = m_RecvDataSem.tryAcquire(1, m_iSyncCallTimeout)) == false && errno == EINTR) continue;	/*!<超时等待同步反馈信号量释放>*/
	if (iSemResult)//收到数据
	{
		bRet = true;
	}
	else
	{
		g_StatusMonitorLog->LogError("[%s][%s][%d] 发送本地数据 failed !", __FILE__, __FUNCTION__, __LINE__);
		CLogNotify::GetInstance().ShowMsgToDataWid("发送本地数据 failed");
		bRet = false;
	}
	m_bIsHaveSendData = false;
	return bRet;
}


void CUdpClient::GetLastCommandData(ST_TRANS_MSG &stlastData)
{
	stlastData = m_stLastSendData;
}

void CUdpClient::ClearList()
{
	m_RecvDataMutex.lock();	/*!<启动互斥锁>*/
	m_listSyncMsgData.clear();
	m_RecvDataMutex.unlock();	/*!<解除互斥锁>*/
}

void CUdpClient::Package(const unsigned char *pszSrcCmd, int iSrcCmdLen, unsigned char *pszDestCmd, int *piDestLen)
{

}

void CUdpClient::UnPackage(const unsigned char*pszSrcPackage, int iSrcPackageLength, unsigned char *pszValidData, int *piValidDataLength)
{

}

bool CUdpClient::CheckMsgHeadAndEnd(const  char * pDataBuf, int iBufLen)
{
	return 0;
}

bool CUdpClient::JudgeIsValidityProtocal(const  char * pDataBuf, int iBufLen)
{
	return true;
}


void CUdpClient::ProServerCmd(RECV_DATA_TYPE enProType, char cCmd)
{

}

RECV_DATA_TYPE CUdpClient::GetMsgType(char cMsgType)
{
	return RECV_TYPE_Unknown;
}



void CUdpClient::PushRecvData(ST_TRANS_MSG stRecvData)
{
	m_RecvDataMutex.lock();
	m_listSyncMsgData.push_back(stRecvData);
	m_RecvDataMutex.unlock();

}

void CUdpClient::PopRecvData(ST_TRANS_MSG &stOutData)
{
	m_RecvDataMutex.lock();
	if (m_listSyncMsgData.size() > 0)
	{
		stOutData = m_listSyncMsgData.front();
		m_listSyncMsgData.pop_front();
	}
	else
	{
		CLogNotify::GetInstance().ShowMsgToDataWid("m_listSyncMsgData is NULL when GetSyncMsgData");
		g_StatusMonitorLog->LogWarn("[%s][%s][%d] m_listSyncMsgData is NULL when GetSyncMsgData", __FILE__, __FUNCTION__, __LINE__);
	}
	m_RecvDataMutex.unlock();
}

int  CUdpClient::SynSendData(ST_TRANS_MSG stMsg,int iTimeout)
{	
	m_UdpMutex.lock();	/*!<启动互斥锁>*/
	//重置同步数据
	ClearList();
	int iRet = 0;
	m_iSyncCallTimeout = iTimeout*1000;
	bool bIsResend = false;
	m_iRetryCounter = 0;
	while (m_iRetryCounter < RESEND_TIMES)
	{
		m_bIsHaveSendData = true;
		bool bSemResult = SendCommand(stMsg.szData, stMsg.iDataLen, m_iSyncCallTimeout, bIsResend);
		if (bSemResult)/*!<收到反馈>*/	/*!<判断结果>*/
		{
			g_StatusMonitorLog->LogFatal("[%s][%s][%d] have Recv Data ......", __FILE__, __FUNCTION__, __LINE__);
			CLogNotify::GetInstance().ShowMsgToDataWid("have Recv Data ......");
			iRet = 0;
			break;
		}
		else	/*!<没有数据的信号发送的信号>*/
		{
			iRet = -1;
			bIsResend = true;
			m_iRetryCounter++;
			CLogNotify::GetInstance().ShowMsgToDataWid("No Recv Data ......");
			g_StatusMonitorLog->LogError("[%s][%s][%d]No Recv Data ...... ", __FILE__, __FUNCTION__, __LINE__);
		}
	}
	m_bIsHaveSendData = false;
	m_UdpMutex.unlock();
	return iRet;
}

int CUdpClient::GetSyncMsgData(ST_TRANS_MSG &stOutData)
{
	PopRecvData(stOutData);
	return 0;
}


bool CUdpClient::AsyncSendData(ST_TRANS_MSG stMsg)
{
	memset(&m_stLastAsyncSendData, 0, sizeof(ST_TRANS_MSG));
	//打包
	Package(stMsg.szData, stMsg.iDataLen, (unsigned char*)m_stLastAsyncSendData.szData, &m_stLastAsyncSendData.iDataLen);
	CPublicFun::ShowDataToLog("Heat", m_stLastAsyncSendData.szData, m_stLastAsyncSendData.iDataLen);
	//发送数据
	int iLen = m_pUdpSocket->writeDatagram((char*)m_stLastAsyncSendData.szData, m_stLastAsyncSendData.iDataLen, QHostAddress(m_strServerIp), m_iServerPort);
	if (iLen != m_stLastAsyncSendData.iDataLen)
	{
		g_StatusMonitorLog->LogError("[%s][%s][%d]haved send len: %d != need send len:%d ", __FILE__, __FUNCTION__, __LINE__, iLen, m_stLastAsyncSendData.iDataLen);
		return false;
	}
	m_stLastAsyncSendData.iMsgType = stMsg.iMsgType;
	CLogNotify::GetInstance().ShowMsgToHeartWid(m_stLastAsyncSendData);
	return true;
}

void CUdpClient::GetNetAddress(ST_COMM_INFO &stCommInfo)
{
	stCommInfo.strServerIP = m_strServerIp;
	stCommInfo.iServerPort = m_iServerPort;
	stCommInfo.iLocalPort = m_iLocalProt;
}

int  CUdpClient::SetNetAddress(ST_COMM_INFO stCommInfo)
{
	if (!stCommInfo.strServerIP.isEmpty())
	{
		m_strServerIp = stCommInfo.strServerIP;
	}
	if (0 != stCommInfo.iServerPort)
	{
		m_iServerPort = stCommInfo.iServerPort;
	}

	if (0 != stCommInfo.iLocalPort)
	{
		m_iLocalProt = stCommInfo.iLocalPort;
	}
	//先断开连接
	CloseComm();
	//绑定本地监听断开
	BindListenPort();
	return 0;
}

void CUdpClient::CloseComm()
{
	m_pUdpSocket->close();

	ST_TRANS_MSG stMsg;
	stMsg.iMsgType = EN_MSG_TYPE_NOTIFY;
	stMsg.iDataLen = sizeof("Close Connect");
	memcpy(stMsg.szData, "Close Connect", sizeof("Close Connect"));
	CLogNotify::GetInstance().ShowMsgToHeartWid(stMsg);
	g_StatusMonitorLog->LogFatal("[%s][%s][%d] CloseComm ", __FILE__, __FUNCTION__, __LINE__);
}

void CUdpClient::SortProData(ST_TRANS_MSG stRecvMsg)
{
	RECV_DATA_TYPE  enRecvDataType = GetMsgType(stRecvMsg.szData[1]);
	if (RECV_TYPE_DATA_ACK_81 == enRecvDataType)
	{
		if (m_bIsHaveSendData)
		{
			//放入接收队列中
			PushRecvData(stRecvMsg);
			m_RecvDataSem.release();//释放信号量
		}
	}
	else if (RECV_TYPE_QUERY_82 == enRecvDataType)
	{
		ProServerCmd(RECV_TYPE_QUERY_82, stRecvMsg.szData[stRecvMsg.iDataLen - 9]);
	}
	else if (RECV_TYPE_CONL_83 == enRecvDataType)
	{
		ProServerCmd(RECV_TYPE_CONL_83, stRecvMsg.szData[stRecvMsg.iDataLen - 9]);
	}
	else //未知数据类型
	{
		CLogNotify::GetInstance().ShowMsgToHeartWid("Recv Unknown Type Data.....");
		g_StatusMonitorLog->LogFatal("[%s][%s][%d] Recv Unknown Type Data ", __FILE__, __FUNCTION__, __LINE__);
	}
}

void CUdpClient::SysTimeWithServer(const  char * pDataBuf, int iBufLen)
{
	if (NULL == pDataBuf || iBufLen < 2)
	{
		return ;
	}

	int iTime = 0;
	memcpy(&iTime, &pDataBuf[14], 4);
	CPublicFun::SetTime(iTime);
	//获取 时间戳 + 2000年1月1日0点0分0秒  的 时间
	QString strServerTime = CPublicFun::GetDateTimeStringByStamp(iTime);
	
	QString strShow = QString("serverStamp: %1 strTime:%2").arg(strServerTime).arg(strServerTime.toStdString().c_str());
	CLogNotify::GetInstance().ShowMsgToHeartWid(strShow);
	g_StatusMonitorLog->LogFatal("[%s][%s][%d] %s ", __FILE__, 
		__FUNCTION__, __LINE__, strShow.toStdString().c_str());
}

void CUdpClient::ProRecvData_Slot()
{
	//hasPendingDatagrams返回true时表示至少有一个数据报在等待被读取
	while (m_pUdpSocket->hasPendingDatagrams())
	{
		QByteArray datagram;
		datagram.resize(m_pUdpSocket->pendingDatagramSize());

		m_pUdpSocket->readDatagram(datagram.data(), datagram.size());

		m_strRecvBuf = datagram.data();
		if (!m_strRecvBuf.isEmpty() && CheckMsgHeadAndEnd(datagram.data(), datagram.size()))
		{
			CPublicFun::ShowDataToLog("RT",(unsigned char*)datagram.data(),datagram.size());
			ST_TRANS_MSG stRecvData;
			stRecvData.iMsgType = EN_MSG_TYPE_RECV;
			//解包
			UnPackage((unsigned char*)datagram.data(), datagram.size(), stRecvData.szData, &stRecvData.iDataLen);
			
			bool bRet = JudgeIsValidityProtocal((char*)stRecvData.szData, stRecvData.iDataLen);
			if (bRet)
			{
				//时间同步
				SysTimeWithServer((char*)stRecvData.szData, stRecvData.iDataLen);
				//分拣处理数据
				SortProData(stRecvData);
			}

			CLogNotify::GetInstance().ShowMsgToDataWid(stRecvData);
		}
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

设备系统软件集成

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值