如果发送端数据发送过块,接收端的数据接受过慢,接受端TCP内部的缓存区域会溢出,无法再发送数据,造成网络阻塞。
所以每次要尽可能的把缓冲区数据读出来,而不是每次读一条消息头。
因此要在应用层设置第二缓冲区,再从第二缓冲区读数据,拆分包
完整图片说明
//缓冲区
char _szRecv[RECV_BUFF_SZIE] = {};
//接收数据 处理粘包 拆分包
int RecvData(ClientSocket* pClient)
{
// 5 接收客户端数据
int nLen = (int)recv(pClient->sockfd(), _szRecv, RECV_BUFF_SZIE, 0);
//printf("nLen=%d\n", nLen);
if (nLen <= 0)
{
printf("客户端<Socket=%d>已退出,任务结束。\n", pClient->sockfd());
return -1;
}
//将收取到的数据拷贝到消息缓冲区
memcpy(pClient->msgBuf() + pClient->getLastPos(), _szRecv, nLen);
//消息缓冲区的数据尾部位置后移
pClient->setLastPos(pClient->getLastPos() + nLen);
//判断消息缓冲区的数据长度大于消息头DataHeader长度
while (pClient->getLastPos() >= sizeof(DataHeader))
{
//这时就可以知道当前消息的长度
DataHeader* header = (DataHeader*)pClient->msgBuf();
//判断消息缓冲区的数据长度大于消息长度
if (pClient->getLastPos() >= header->dataLength)
{
//消息缓冲区剩余未处理数据的长度
int nSize = pClient->getLastPos() - header->dataLength;
//处理网络消息
OnNetMsg(pClient->sockfd(),header);
//将消息缓冲区剩余未处理数据前移
memcpy(pClient->msgBuf(), pClient->msgBuf() + header->dataLength, nSize);
//消息缓冲区的数据尾部位置前移
pClient->setLastPos(nSize);
}
else {
//消息缓冲区剩余数据不够一条完整消息
break;
}
}
return 0;
}
首先用一个缓冲区直接读出数据,拷贝到消息缓冲区,再从消息缓冲区中读出消息头
//响应网络消息
virtual void OnNetMsg(SOCKET cSock, DataHeader* header)
{
switch (header->cmd)
{
case CMD_LOGIN:
{
Login* login = (Login*)header;
//printf("收到客户端<Socket=%d>请求:CMD_LOGIN,数据长度:%d,userName=%s PassWord=%s\n", cSock, login->dataLength, login->userName, login->PassWord);
//忽略判断用户密码是否正确的过程
LoginResult ret;
SendData(cSock, &ret);
}
break;
case CMD_LOGOUT:
{
Logout* logout = (Logout*)header;
//printf("收到客户端<Socket=%d>请求:CMD_LOGOUT,数据长度:%d,userName=%s \n", cSock, logout->dataLength, logout->userName);
//忽略判断用户密码是否正确的过程
LogoutResult ret;
SendData(cSock, &ret);
}
break;
default:
{
printf("<socket=%d>收到未定义消息,数据长度:%d\n", cSock, header->dataLength);
//DataHeader ret;
//SendData(cSock, &ret);
}
break;
}
}
解决粘包的拆分包读取策略
//缓冲区最小单元大小
#ifndef RECV_BUFF_SZIE
#define RECV_BUFF_SZIE 102400
#endif // !RECV_BUFF_SZIE
//第二缓冲区 消息缓冲区
char _szMsgBuf[RECV_BUFF_SZIE * 10] = {};
//消息缓冲区的数据尾部位置
int _lastPos = 0;
//接收缓冲区
char _szRecv[RECV_BUFF_SZIE] = {};
//接收数据 处理粘包 拆分包
int RecvData(SOCKET cSock)
{
// 5 接收数据
int nLen = (int)recv(cSock, _szRecv, RECV_BUFF_SZIE, 0);
//printf("nLen=%d\n", nLen);
if (nLen <= 0)
{
printf("<socket=%d>与服务器断开连接,任务结束。\n", cSock);
return -1;
}
//将收取到的数据拷贝到消息缓冲区
memcpy(_szMsgBuf+_lastPos, _szRecv, nLen);
//消息缓冲区的数据尾部位置后移
_lastPos += nLen;
//判断消息缓冲区的数据长度大于消息头DataHeader长度
while (_lastPos >= sizeof(DataHeader))
{
//这时就可以知道当前消息的长度
DataHeader* header = (DataHeader*)_szMsgBuf;
//判断消息缓冲区的数据长度大于消息长度
if (_lastPos >= header->dataLength)
{
//消息缓冲区剩余未处理数据的长度
int nSize = _lastPos - header->dataLength;
//处理网络消息
OnNetMsg(header);
//将消息缓冲区剩余未处理数据前移
memcpy(_szMsgBuf, _szMsgBuf + header->dataLength, nSize);
//消息缓冲区的数据尾部位置前移
_lastPos = nSize;
}
else {
//消息缓冲区剩余数据不够一条完整消息
break;
}
}
return 0;
}