日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

C++實現TCP客戶端及服務器Recv數據篩選處理詳解_C 語言

作者:中國好公民st ? 更新時間: 2022-11-21 編程語言

正文

對于一個簡單的tcp通訊這里我就不再講述了,今天主要為大家講解下,如何從::recv中篩選出一個完整包邏輯。

就簡單的以客戶端為例(服務器接收方也是同樣的邏輯),假設服務器一直在頻繁發送數據,在recv函數中并不能保證每次接收的都是一個完整的包,當設置recv的緩沖區過大時,就會出現多個包同時接收的問題。

對于這種情況,初出茅廬的我們有時會想不到居然還有多個包共同出現的問題,甚至有些還沒學會如何高效的分離出一個有效的數據包。

一般在進行tcp通訊協議時,為了各個指令的區分,通常都會用以下方式進行發送,如下圖:

具體的通信協議規格可以按照各個業務需求來定義,這里只是列舉了一個簡單的例子。

在這篇文章中根據上圖中的協議格式,進行舉例講解。

協議標識 測試具體
系統標識 0x5A
源設備標識 0xFCFB
預留字節 假設占10個字節
源設備編碼 根據連接的服務端設備編碼,假設這是是0x01(編號1)
命令字 具體的業務指令,兩個字節
數據長度 需要發送的數據長度
數據內容 具體業務具體內容
校驗碼 0xEF

在進行tcp通訊時,我們每條指令都是按照這種方式進行發送的,接下來,到了文章的重點部分了,該如何對接收的數據篩選?

根據流程圖可以發現,當數據不匹配時,需要重新進行篩選,程序采用了遞歸的方式進行數據剔除。這里只是展示了內部的流程處理,外部調用該如何呢?

int nTotalSize = 0;
while(this->JudgeValidData(vetRecvData, nTotalSize) == ture)
{
    //接收到了完整包,對包進行處理
}

代碼解析

nTotalSize:代表了一個完整數據包的總長度。

vetRecvData:recv函數接收的所有緩沖區內容。

其實上面流程圖的內容就是JudgeValidData()的函數處理邏輯。

對于該函數的詳細解析:

1:當Tcp緩沖區的數據小于3個字節時,不進行判斷

此時字節數據過小,無法判斷是不是當前程序中需要的

2:判斷包頭是不是一致?

上述表格中,舉例說明了包頭的定義,分別是:0x5A、0xFC、0xFB

那么,我們在判斷數據時首先要判斷包頭是否正確?包頭不正確后續也就不需要判斷了!

char ch0 = vetRecvData[0]; //系統標識
char ch1 = vetRecvData[1]; //標識1
char ch2 = vetRecvData[2]; //標識2
if((ch0 == 0x5A) && (ch1 == 0xFC) && (ch2 == 0xFB))
{
    //包頭正確,進行后續處理
}
else
{
    //包頭不正確,剔除數據,默認剔除緩沖區的第一個字節數據
    std::vector<char>::iterator itbegin = vetRecvData.begin();
    vetRecvData.erase(itbegin);
    //重新調用當前函數,遞歸判斷
    return JudgeValidData(vetRecvData, nTotalSize);
}

3:包頭匹配后,判斷是否達到了數據包的固定協議長度?

在這里說的固定協議長度,就是數據內容之前的字節,在這篇文章中,數據內容之前有19個字節,所以,當vetRecvData的數據總長度 < 19個字節時,不需要處理。

當達到固定協議長度后,說明該緩沖區中存在了有效數據,那么就需要判斷實際的數據內容是否達到了整包的標準?

4:判斷有效數據是否接收完整?

首先,需要獲取當前包需要接收的數據長度。在這里是占用三個字節,分別是:16、17、18這三位

int ndatalen = (unsigned char)pThreadParam->vetRecvData[16] * 256 *256+ (unsigned char)pThreadParam->vetRecvData[17]*256 + (unsigned char)pThreadParam->vetRecvData[18];
nTotalSize = 19 + ndatalen + 1;

如果當前vetRecvData緩沖區的數據 < nTotalSize,說明數據不完整,不判斷;反之,緩沖區中存在完整的數據。

5:數據校驗位判斷

最后,判斷下緩沖區完整包的最后一位是否是校驗位?

這樣,才算是一個完整的一條數據包!

到這里,就算判斷完成了,由此可以從一個大的緩沖區中抽取出有用的一組數據。

內部的判斷邏輯講解完之后,接下來就是外部數據使用了。繼續上面的這段代碼繼續講解~

int nTotalSize = 0;
while(this->JudgeValidData(vetRecvData, nTotalSize) == ture)
{
    //接收到了完整包,對包進行處理
}

接收到一個完整包之后,數據處理過程

1:根據nTotalSize大小從緩沖區中獲取有效內容

2:截取出剩余的緩沖區內容,重新賦值

3:對一個完整包數據進行業務處理

將流程轉換成代碼,如下:

int nTotalSize = 0;
while (this->JudgeValidData(vetRecvData, nTotalSize) == true)
{
	//這一條數據被處理之后,使用一個臨時容器存儲 處理的 通訊數據
	std::vector<char> vetValidData;
	char* chUseData = new char[nTotalSize];
	memset(chUseData, 0, nTotalSize);
	for (int i = 0; i < vetRecvData.size(); i++)
	{
		if (i < nTotalSize)
		{
			chUseData[i] = vetRecvData[i];
		}
		else
			vetValidData.push_back(vetRecvData[i]);
	}
	//刪除 存儲所有接收字節的容器 中的數據
	vetRecvData.clear();
	vetRecvData = vetValidData; //重新更新數據信息
	//接收的是整包數據時,進行實際的數據處理
	ProcessingPancelValidData(chUseData, nTotalSize);
	delete[] chUseData;
	chUseData = nullptr;
}

原文鏈接:https://juejin.cn/post/7150979561390505991

欄目分類
最近更新