欧美free性护士vide0shd,老熟女,一区二区三区,久久久久夜夜夜精品国产,久久久久久综合网天天,欧美成人护士h版

首頁綜合 正文
目錄

柚子快報邀請碼778899分享:Qt——TCP UDP網(wǎng)絡編程

柚子快報邀請碼778899分享:Qt——TCP UDP網(wǎng)絡編程

http://yzkb.51969.com/

目錄

前言正文一、TCP二、UDP1、基本流程2、必備知識

三、代碼層級1、UDP服務端

END、總結的知識與問題1、如何獲取QByteArray中某一字節(jié)的數(shù)據(jù),并將其轉為十進制?2、如何以本年本月本日為基礎,獲取時間戳,而不以1970為基礎?3、如何將一個四個字節(jié)組成的數(shù)拆分成1個字節(jié)一個字節(jié)的?4、如何對前面的所有字節(jié)進行異或校驗?5、如何將QByteArray中的某個字節(jié)轉為十六進制?

參考

前言

恰好,有個項目需要用到UDP,之前使用比較多的是TCP,UDP的還是第一次搞,但是感覺流程也是差不多,甚至比TCP要更簡單就行,這里就稍微做一下總結,寫一下自己需要注意的點,以及從網(wǎng)上查找到的,比較有用的一些資料。本篇文章,主要偏向的還是UDP,TCP的話,大家可以稍微看下,當做過下眼。

正文

一、TCP

TCP(Transmission Control Protocol,傳輸控制協(xié)議)是一種面向連接的、可靠的傳輸協(xié)議,它是OSI(Open System Interconnection,開放式系統(tǒng)互聯(lián))模型中的第四層協(xié)議,通常使用于網(wǎng)絡中的應用層和傳輸層之間。

? TCP建立連接主要就是三次握手、斷開連接主要就是四次揮手。

三次握手的基本流程如下: 四次揮手的基本流程如下:

關于TCP Socket 的實現(xiàn)邏輯如下:

本篇文章關于TCP就點到為止了。下面就講下UDP了。

二、UDP

1、基本流程

UDP(User Data Protocol),用戶數(shù)據(jù)報協(xié)議,是一種簡單輕量級、不可靠、面向數(shù)據(jù)報、無連接的傳輸層協(xié)議,可以應用在可靠性不是十分重要的場合,如短消息、廣播信息等。

適用于以下幾種情況:

A、網(wǎng)絡數(shù)據(jù)大多為短消息。

B、擁有大量客戶端

C、對數(shù)據(jù)安全性無特殊要求

D、網(wǎng)絡負擔非常重,但對響應速度要求高。

這部分內容,應該有一部分難點在于UDP的拆包上面,但由于目前解除的項目不需要拆包,就暫不考慮這個問題了。

UDP編程的流程:

2、必備知識

1、數(shù)據(jù)報的長度一般不少于512字節(jié),每個數(shù)據(jù)報包含發(fā)送者和接收者的IP地址和端口等信息。 2、單播、廣播、組播的知識

單播(unicast)模式:一個UDP客戶端發(fā)出的數(shù)據(jù)報只發(fā)送到另一個指定地址和端口的UDP客戶端,是一對一的數(shù)據(jù)傳輸。 廣播(broadcast)模式:一個UDP客戶端發(fā)出的數(shù)據(jù)報,在同一網(wǎng)絡范圍內其他所有的UDP客戶端都可以收到。QUdpSocket支持IPv4廣播。廣播經(jīng)常用于實現(xiàn)網(wǎng)絡發(fā)現(xiàn)的協(xié)議。要獲取廣播數(shù)據(jù)只需在數(shù)據(jù)報中指定接收端地址為QHostAddress::Broadcast,一般的廣播地址為255.255.255.255。 組播(multicast)模式:也稱為多播。UDP客戶端加入到另一組播IP地址指定的多播組,成員向多組播地址發(fā)送的數(shù)據(jù)報組內成員都可以接收到,類似于QQ群的功能。

單播(Unicast):單播是一種點對點的通信方式,其中一個發(fā)送方(源)向一個接收方(目標)發(fā)送數(shù)據(jù)。在單播通信中,數(shù)據(jù)從發(fā)送方經(jīng)過網(wǎng)絡傳輸?shù)街付ǖ慕邮辗?,其他設備不會接收到該數(shù)據(jù)。單播適用于需要將數(shù)據(jù)傳輸?shù)教囟ㄔO備或主機的場景,例如客戶端-服務器通信。 廣播(Broadcast):廣播是一種一對多的通信方式,其中一個發(fā)送方向局域網(wǎng)中的所有設備發(fā)送數(shù)據(jù)。在廣播通信中,數(shù)據(jù)從發(fā)送方通過網(wǎng)絡傳輸?shù)酵痪钟蚓W(wǎng)中的所有設備。所有接收方都會接收到廣播數(shù)據(jù)。廣播適用于需要將數(shù)據(jù)傳輸?shù)骄钟蚓W(wǎng)中的所有設備的場景,例如局域網(wǎng)上的服務發(fā)現(xiàn)、網(wǎng)絡廣告等。 廣播UDP與單播UDP的區(qū)別就是IP地址不同,廣播使用廣播地址255.255.255.255,將消息發(fā)送到在同一廣播網(wǎng)絡上的每個主機。值得強調的是:本地廣播信息是不會被路由器轉發(fā)。當然這是十分容易理解的,因為如果路由器轉發(fā)了廣播信息,那么勢必會引起網(wǎng)絡癱瘓。 其實廣播顧名思義,就是想局域網(wǎng)內所有的人說話,但是廣播還是要指明接收者的端口號的,因為不可能接受者的所有端口都來收聽廣播。 3、報文大小的限制與各系統(tǒng)的協(xié)議實現(xiàn)有關,但不得超過其下層 IP 協(xié)議規(guī)定的64KB 4、所謂客戶端進行廣播,是指客戶端向一個廣播地址255.255.255.255 + 一個指定的服務端監(jiān)聽的端口號 進行發(fā)送數(shù)據(jù)。只要端口號是對的,那么服務端就會接收到數(shù)據(jù)。 5、如何監(jiān)聽某個端口:netstat -ano | findstr 1024 6、

三、代碼層級

1、UDP服務端

該代碼是我實現(xiàn)的UDP服務端,只負責接收信息。 UDPServer.h

#ifndef UDPSERVER_H

#define UDPSERVER_H

#include

#include

class CUdpServer : public QObject

{

Q_OBJECT

public:

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

#include

#include

#include

CUdpServer::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<<"||"<

cArray[4] = (int)byte1;//時間戳

cArray[5] = (int)byte2;//時間戳

cArray[6] = (int)byte3;//時間戳

cArray[7] = (int)byte4;//時間戳

cArray[8] = _oArray.at(8);//唯一ID

cArray[9] = _oArray.at(9);//唯一ID

cArray[10] = _oArray.at(10);//唯一ID

cArray[11] = _oArray.at(11);//唯一ID

bool bOK = false;

cArray[12] = _sCmd.toInt(&bOK, 16);//命令類型

cArray[13] = 0x00;//包序列

cArray[14] = 0x00;//數(shù)據(jù)長度

cArray[15] = _XorCheck(cArray, sizeof(cArray) - 1);//校驗字節(jié)

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

// 創(chuàng)建基準日期(以當前年份為基準)

QDateTime baseDateTime(QDate(iCurrentYear, iCurrentMonth, iCurrentDay), QTime(0, 0, 0));

// 計算當前時間相對于基準日期的毫秒數(shù)

qint64 timestamp = currentDateTime.toMSecsSinceEpoch() - baseDateTime.toMSecsSinceEpoch();

return timestamp;

}

void CUdpServer::on_readyRead()

{

//幀頭(af) 設備類型 設備編號 電池電量 時間戳 唯一ID 命令類型 包序列 數(shù)據(jù)長度 校驗字節(jié) 數(shù)據(jù)校驗 數(shù)據(jù)

// 1 1 1 1 4 4 1 1 1 1 1 N

while (m_pUdpServer->hasPendingDatagrams()) // 判斷是否有可讀數(shù)據(jù)

{

QNetworkDatagram datagram = m_pUdpServer->receiveDatagram(); // 讀取數(shù)據(jù)

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中某一字節(jié)的數(shù)據(jù),并將其轉為十進制?

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

// 創(chuàng)建基準日期(以當前年份為基準)

QDateTime baseDateTime(QDate(iCurrentYear, iCurrentMonth, iCurrentDay), QTime(0, 0, 0));

// 計算當前時間相對于基準日期的毫秒數(shù)

qint64 timestamp = currentDateTime.toMSecsSinceEpoch() - baseDateTime.toMSecsSinceEpoch();

return timestamp;

}

3、如何將一個四個字節(jié)組成的數(shù)拆分成1個字節(jié)一個字節(jié)的?

qint64 iTimeStamp = _GetNowDateTimeStampMs();//這個實際獲取到的是4個字節(jié),但也沒關系,我們根據(jù)下面的方式,取到的一定是最前面的四個字節(jié)

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、如何對前面的所有字節(jié)進行異或校驗?

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中的某個字節(jié)轉為十六進制?

char byte = replyData.at(i);//replayData就是一個QByteArray

QString hexString = QString("%1").arg((int)byte, 2, 16, QChar('0'));

參考

參考: 1、UDP廣播與多播 2、Qt 網(wǎng)絡編程-UDP 3、Qt中實現(xiàn)UDP的分包和組包——參考“草上爬”的博客

柚子快報邀請碼778899分享:Qt——TCP UDP網(wǎng)絡編程

http://yzkb.51969.com/

好文推薦

評論可見,查看隱藏內容

本文內容根據(jù)網(wǎng)絡資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。

轉載請注明,如有侵權,聯(lián)系刪除。

本文鏈接:http://gantiao.com.cn/post/18524892.html

發(fā)布評論

您暫未設置收款碼

請在主題配置——文章設置里上傳

掃描二維碼手機訪問

文章目錄