網站首頁 編程語言 正文
正文
對于一個簡單的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
相關推薦
- 2022-06-10 ASP.NET?Core使用EF保存數據、級聯刪除和事務使用_實用技巧
- 2022-12-30 React?Context詳解使用方法_React
- 2022-11-01 C++中的pair使用詳解_C 語言
- 2022-06-02 Pandas實現DataFrame的簡單運算、統計與排序_python
- 2022-11-02 golang服務報錯:?write:?broken?pipe的解決方案_Golang
- 2022-01-30 VSCode標簽內的代碼塊無法折疊問題解決
- 2022-10-11 Laravel 不添加配置文件實現 Redis 切換庫和設置 key 前綴
- 2021-12-10 解決線上Oracle連接耗時過長的問題現象_oracle
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支