自己设置免费网站设计平台,网站制作流程是什么,企业网站建立步骤,h5网页制作平台目录 前言正文一、TCP二、UDP1、基本流程2、必备知识 三、代码层级1、UDP服务端 END、总结的知识与问题1、如何获取QByteArray中某一字节的数据#xff0c;并将其转为十进制#xff1f;2、如何以本年本月本日为基础#xff0c;获取时间戳#xff0c;而不以1970为基础#… 目录 前言正文一、TCP二、UDP1、基本流程2、必备知识 三、代码层级1、UDP服务端 END、总结的知识与问题1、如何获取QByteArray中某一字节的数据并将其转为十进制2、如何以本年本月本日为基础获取时间戳而不以1970为基础3、如何将一个四个字节组成的数拆分成1个字节一个字节的4、如何对前面的所有字节进行异或校验5、如何将QByteArray中的某个字节转为十六进制 参考 前言
恰好有个项目需要用到UDP之前使用比较多的是TCPUDP的还是第一次搞但是感觉流程也是差不多甚至比TCP要更简单就行这里就稍微做一下总结写一下自己需要注意的点以及从网上查找到的比较有用的一些资料。本篇文章主要偏向的还是UDPTCP的话大家可以稍微看下当做过下眼。
正文
一、TCP
TCPTransmission Control Protocol传输控制协议是一种面向连接的、可靠的传输协议它是OSIOpen System Interconnection开放式系统互联模型中的第四层协议通常使用于网络中的应用层和传输层之间。
TCP建立连接主要就是三次握手、断开连接主要就是四次挥手。
三次握手的基本流程如下 四次挥手的基本流程如下 关于TCP Socket 的实现逻辑如下 本篇文章关于TCP就点到为止了。下面就讲下UDP了。
二、UDP
1、基本流程
UDPUser Data Protocol用户数据报协议是一种简单轻量级、不可靠、面向数据报、无连接的传输层协议可以应用在可靠性不是十分重要的场合如短消息、广播信息等。
适用于以下几种情况 A、网络数据大多为短消息。 B、拥有大量客户端 C、对数据安全性无特殊要求 D、网络负担非常重但对响应速度要求高。 这部分内容应该有一部分难点在于UDP的拆包上面但由于目前解除的项目不需要拆包就暂不考虑这个问题了。
UDP编程的流程 2、必备知识
1、数据报的长度一般不少于512字节每个数据报包含发送者和接收者的IP地址和端口等信息。 2、单播、广播、组播的知识 单播unicast模式一个UDP客户端发出的数据报只发送到另一个指定地址和端口的UDP客户端是一对一的数据传输。 广播broadcast模式一个UDP客户端发出的数据报在同一网络范围内其他所有的UDP客户端都可以收到。QUdpSocket支持IPv4广播。广播经常用于实现网络发现的协议。要获取广播数据只需在数据报中指定接收端地址为QHostAddress::Broadcast,一般的广播地址为255.255.255.255。 组播multicast模式也称为多播。UDP客户端加入到另一组播IP地址指定的多播组成员向多组播地址发送的数据报组内成员都可以接收到类似于QQ群的功能。 单播Unicast单播是一种点对点的通信方式其中一个发送方源向一个接收方目标发送数据。在单播通信中数据从发送方经过网络传输到指定的接收方其他设备不会接收到该数据。单播适用于需要将数据传输到特定设备或主机的场景例如客户端-服务器通信。 广播Broadcast广播是一种一对多的通信方式其中一个发送方向局域网中的所有设备发送数据。在广播通信中数据从发送方通过网络传输到同一局域网中的所有设备。所有接收方都会接收到广播数据。广播适用于需要将数据传输到局域网中的所有设备的场景例如局域网上的服务发现、网络广告等。 广播UDP与单播UDP的区别就是IP地址不同广播使用广播地址255.255.255.255将消息发送到在同一广播网络上的每个主机。值得强调的是本地广播信息是不会被路由器转发。当然这是十分容易理解的因为如果路由器转发了广播信息那么势必会引起网络瘫痪。 其实广播顾名思义就是想局域网内所有的人说话但是广播还是要指明接收者的端口号的因为不可能接受者的所有端口都来收听广播。 3、报文大小的限制与各系统的协议实现有关但不得超过其下层 IP 协议规定的64KB 4、所谓客户端进行广播是指客户端向一个广播地址255.255.255.255 一个指定的服务端监听的端口号 进行发送数据。只要端口号是对的那么服务端就会接收到数据。 5、如何监听某个端口netstat -ano | findstr 1024 6、
三、代码层级
1、UDP服务端
该代码是我实现的UDP服务端只负责接收信息。 UDPServer.h
#ifndef UDPSERVER_H
#define UDPSERVER_H#include QObject
#include QUdpSocketclass CUdpServer : public QObject
{Q_OBJECTpublic:explicit CUdpServer(QObject *parent nullptr);~CUdpServer();bool OpenUdpServer(const int _iPort);
protected:bool _WriteDatagram(const QString _sIp, const int _iPort, char *_pData, int _iLength);bool _ResponseFrame(const QString _sIp, const int _iPort, const QString _sCmd, const QByteArray _oArray);bool _HandleLoginFrame(const QString _sIp, const int _iPort, const QByteArray _oArray);
private:void _Init();uint8_t _XorCheck(uint8_t *pbuf, uint32_t length);qint64 _GetNowDateTimeStampMs();public slots:void on_readyRead();
private:QUdpSocket* m_pUdpServer nullptr;bool m_bOpen false;bool m_bDoing false;int m_iElectricQuantity 0;
};#endif // UDPSERVER_H
UDPServer.cpp
#include UdpServer.h#include QNetworkDatagram
#include QDateTime#include CommonFunc.h
#include KldLog.hCUdpServer::CUdpServer(QObject *parent):QObject(parent)
{_Init();connect(m_pUdpServer, QUdpSocket::readyRead, this, CUdpServer::on_readyRead);
}CUdpServer::~CUdpServer()
{}bool CUdpServer::OpenUdpServer(const int _iPort)
{bool bRet false;if (false m_bOpen){bRet m_pUdpServer-bind(QHostAddress::Any, _iPort);if (bRet){m_bOpen true;}}return bRet;
}bool CUdpServer::_WriteDatagram(const QString _sIp, const int _iPort, char *_pData, int _iLength)
{bool bRet false;QByteArray oOutputArray QByteArray::fromRawData((char*)_pData, _iLength);qint64 iLen m_pUdpServer-writeDatagram(oOutputArray, QHostAddress(_sIp), _iPort);return bRet;
}bool CUdpServer::_ResponseFrame(const QString _sIp, const int _iPort, const QString _sCmd, const QByteArray _oArray)
{bool bRet false;int iElectricQuantity (int)_oArray.at(3);uchar cArray[16] {0};cArray[0] 0xAF;//帧头cArray[1] 0x00;//设备类型cArray[2] 0x00;//设备编号cArray[3] 0x64;//电池电量qint64 iTimeStamp _GetNowDateTimeStampMs();quint8 byte1 (iTimeStamp 24) 0xFF;quint8 byte2 (iTimeStamp 16) 0xFF;quint8 byte3 (iTimeStamp 8) 0xFF;quint8 byte4 iTimeStamp 0xFF;LOG_INFO ---z CUdpServer::_HandleHeartFrame byte1: iTimeStamp||QString::number(iTimeStamp, 16).toStdString();cArray[4] (int)byte1;//时间戳cArray[5] (int)byte2;//时间戳cArray[6] (int)byte3;//时间戳cArray[7] (int)byte4;//时间戳cArray[8] _oArray.at(8);//唯一IDcArray[9] _oArray.at(9);//唯一IDcArray[10] _oArray.at(10);//唯一IDcArray[11] _oArray.at(11);//唯一IDbool bOK false;cArray[12] _sCmd.toInt(bOK, 16);//命令类型cArray[13] 0x00;//包序列cArray[14] 0x00;//数据长度cArray[15] _XorCheck(cArray, sizeof(cArray) - 1);//校验字节bRet _WriteDatagram(_sIp, _iPort, (char*)cArray, sizeof(cArray));return bRet;
}bool CUdpServer::_HandleLoginFrame(const QString _sIp, const int _iPort, const QByteArray _oArray)
{bool bRet false;if (_oArray.length() 16){return bRet;}if (m_bDoing){return false;}m_bDoing true;bRet _ResponseFrame(_sIp, _iPort, 01, _oArray);m_bDoing false;return bRet;
}void CUdpServer::_Init()
{m_pUdpServer new QUdpSocket(this);
}uint8_t CUdpServer::_XorCheck(uint8_t *pbuf, uint32_t length)
{uint8_t temp 0;uint32_t i;pbuf;pbuf;for (i 0; i length - 2; i){temp ^ *pbuf;}return temp;
}qint64 CUdpServer::_GetNowDateTimeStampMs()
{// 获取当前日期和时间QDateTime currentDateTime QDateTime::currentDateTime();// 获取当前年份int iCurrentYear currentDateTime.date().year();int iCurrentMonth currentDateTime.date().month();int iCurrentDay currentDateTime.date().day();// 创建基准日期以当前年份为基准QDateTime baseDateTime(QDate(iCurrentYear, iCurrentMonth, iCurrentDay), QTime(0, 0, 0));// 计算当前时间相对于基准日期的毫秒数qint64 timestamp currentDateTime.toMSecsSinceEpoch() - baseDateTime.toMSecsSinceEpoch();return timestamp;
}void CUdpServer::on_readyRead()
{//帧头(af) 设备类型 设备编号 电池电量 时间戳 唯一ID 命令类型 包序列 数据长度 校验字节 数据校验 数据// 1 1 1 1 4 4 1 1 1 1 1 Nwhile (m_pUdpServer-hasPendingDatagrams()) // 判断是否有可读数据{QNetworkDatagram datagram m_pUdpServer-receiveDatagram(); // 读取数据QByteArray replyData datagram.data();if(replyData.count()){QString sIP datagram.senderAddress().toString().remove(::ffff:);int iPort datagram.senderPort();QString sCmd QString(%1).arg((char)replyData.at(12), 2, 16, QChar(0));for (int i 0; i replyData.size(); i){char byte replyData.at(i);QString sByte QString::number((int)byte, 16);QString hexString QString(%1).arg((int)byte, 2, 16, QChar(0));}if (sCmd 01){_HandleLoginFrame(sIP, iPort, replyData);}}}
}
调用的时候就直接new一个这个UDPServer的对象就可以了。
如果大家需要直接可以运行起来的程序大家可以参考这位兄弟的这位兄弟的就写的比较完整了客户端服务端都有。 https://github.com/mahuifa/QMDemo/tree/master/QMNetwork
END、总结的知识与问题
1、如何获取QByteArray中某一字节的数据并将其转为十进制
int iElectricQuantity (int)_oArray.at(3);2、如何以本年本月本日为基础获取时间戳而不以1970为基础
qint64 CUdpServer::_GetNowDateTimeStampMs()
{// 获取当前日期和时间QDateTime currentDateTime QDateTime::currentDateTime();// 获取当前年份int iCurrentYear currentDateTime.date().year();int iCurrentMonth currentDateTime.date().month();int iCurrentDay currentDateTime.date().day();// 创建基准日期以当前年份为基准QDateTime baseDateTime(QDate(iCurrentYear, iCurrentMonth, iCurrentDay), QTime(0, 0, 0));// 计算当前时间相对于基准日期的毫秒数qint64 timestamp currentDateTime.toMSecsSinceEpoch() - baseDateTime.toMSecsSinceEpoch();return timestamp;
}
3、如何将一个四个字节组成的数拆分成1个字节一个字节的 qint64 iTimeStamp _GetNowDateTimeStampMs();//这个实际获取到的是4个字节但也没关系我们根据下面的方式取到的一定是最前面的四个字节quint8 byte1 (iTimeStamp 24) 0xFF;quint8 byte2 (iTimeStamp 16) 0xFF;quint8 byte3 (iTimeStamp 8) 0xFF;quint8 byte4 iTimeStamp 0xFF;cArray[4] (int)byte1;//时间戳cArray[5] (int)byte2;//时间戳cArray[6] (int)byte3;//时间戳cArray[7] (int)byte4;//时间戳4、如何对前面的所有字节进行异或校验
uint8_t CUdpServer::_XorCheck(uint8_t *pbuf, uint32_t length)
{uint8_t temp 0;uint32_t i;pbuf;pbuf;for (i 0; i length - 2; i){temp ^ *pbuf;}return temp;
}
5、如何将QByteArray中的某个字节转为十六进制
char byte replyData.at(i);//replayData就是一个QByteArray
QString hexString QString(%1).arg((int)byte, 2, 16, QChar(0));参考
参考 1、UDP广播与多播 2、Qt 网络编程-UDP 3、Qt中实现UDP的分包和组包——参考“草上爬”的博客