一 现象:
粘包:
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循环里面处理数据,直到把每一包数据都分开并处理完后跳出。
原文链接: https://www.cnblogs.com/zwj-199306231519/p/13699982.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/203006
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!