一 現象:
粘包:
A機器發出2包數據,B機器把2包數據作為一次收到,此時2包數據粘在一起。
分包:
A機器發送1包數據,B機器分為兩次收到這包數據,此時,這1報數據分為2次被B機器收到。
二 產生原因:
當服務端和客戶端用到TCP通信時,可能會有以下場景(1)網絡有延遲、(2)客戶端頻繁發送的數據包給服務端,(3)TCP自身機制(需要等自己緩沖區滿后,才發送一包數據),由於這些原因會導致粘包,服務端一次收到的數據中包含多個數據包,此時就得分包處理數據。
三 處理粘包分包代碼:
(1)協議:
(2)代碼:
#include "stdafx.h" #include"windows.h" #include<iostream> #define CHONG_QING_SIX_LOCK_FRAME_MAX_LENGTH 4096 #define CHONG_QING_SIX_LOCK_DATA_HEADER_LENGTH 6 int RecvData() { DWORD recv_len = 0; int dataLength = 0; int sumDataLength = 0; int nRemainSize = 0; int lastPos = 0; BYTE recvbuf[CHONG_QING_SIX_LOCK_FRAME_MAX_LENGTH], databuf[CHONG_QING_SIX_LOCK_FRAME_MAX_LENGTH]; char oneFrameData[1024]; memset(recvbuf, 0, sizeof(recvbuf)); memset(databuf, 0, sizeof(databuf)); //收到服務端消息 //接受數據,處理粘包,拆分包 recv_len = (int)recv(m_Socket, (char *)recvbuf, CHONG_QING_SIX_LOCK_FRAME_MAX_LENGTH, 0); if (recv_len > 0) { memcpy(databuf + lastPos, recvbuf, recv_len); lastPos += recv_len; //判斷消息緩沖區的數據長度大於消息頭 while (lastPos >= CHONG_QING_SIX_LOCK_DATA_HEADER_LENGTH) { //包頭做判斷,如果包頭錯誤,收到的數據全部清空 if (databuf[0] == 0xEF && databuf[1] == 0xEF && databuf[2] == 0xEF && databuf[3] == 0xEF) { dataLength = MAKEWORD(databuf[4], databuf[5]); sumDataLength = CHONG_QING_SIX_LOCK_DATA_HEADER_LENGTH + dataLength + 6; //判斷消息緩沖區的數據長度大於消息體 if (lastPos >= sumDataLength) { //CRC校驗 if (CheckSum((byte *)databuf, dataLength + CHONG_QING_SIX_LOCK_DATA_HEADER_LENGTH + 2)) { memcpy(oneFrameData, databuf, sumDataLength); //處理數據 DealData(oneFrameData); //剩余未處理消息緩沖區數據的長度 nRemainSize = lastPos - sumDataLength; //將未處理的數據前移 if (nRemainSize > 0) { memcpy(databuf, databuf + (dataLength + CHONG_QING_SIX_LOCK_DATA_HEADER_LENGTH + 6), nRemainSize); lastPos = nRemainSize; } } else { if (nRemainSize > 0) { memcpy(databuf, databuf + sumDataLength, nRemainSize); } lastPos = nRemainSize; } } else { break; } } else //尋找下一個包頭 { BOOL isFind = FALSE; int nFindStart = 0; for (int k = 1; k < lastPos; k++) { if (databuf[k] == 0xEF && databuf[k + 1] == 0xEF && databuf[k + 2] == 0xEF && databuf[k + 3] == 0xEF) { nFindStart = k; isFind = TRUE; break; } } if (isFind == TRUE) { memcpy(databuf, databuf + nFindStart, lastPos - nFindStart); lastPos = lastPos - nFindStart; } else { memset(databuf, 0, sizeof(databuf)); lastPos = 0; break; } } } } return 0; } int _tmain(int argc, _TCHAR* argv[]) { system("pause"); return 0; }
四 對以上代碼處理流程解釋:
1)可以看到以上代碼分為2個緩沖區,第一個只負責接收數據,第二個負責處理數據。
2)當服務端收到一包數據時,2種情況時,將處理數據的緩沖區的數據全部清空,(1)當出現包頭驗證錯誤,(2)CRC校驗失敗。
3)當接受到的數據長度大於包頭,但是不大於整個消息體的長度時,保存前面接收的數據后,跳出while循環,繼續接受數據,
4)當接收到的數據是多包數據時,它會一直在while循環里面處理數據,直到把每一包數據都分開並處理完后跳出。