大家都知道,网络tcp,流socket是不能保证收到的包是逻辑完整的,也许一个逻辑完整的被分成两段发送,所以我们使用流socket就需要将接收的包(物理包),经过整理变成逻辑包。然后再处理。这个整理包的函数就是所说的拼包函数。下面的代码用环形缓冲实现了一个拼包函数。环形缓冲可以减少不必要memcpy,从而提高效率。
// 这是per-I/O数据。它包含了在套节字上处理I/O操作的必要信息
struct TPerIoData
{
OVERLAPPED ol; //重叠io,必须为第一个数据
WSABUF dataBuf; //投递数据时的结构
char buff[IO_BUF_LEN]; //接收或发送的io数据的buffer
int bufferLen; //buffer长度
SOCKET sClient; // 在Accept一个连接后,通过它传递连接的客户端
int opType; // 操作类型:Operation_type
};
// 这是per-Handle数据。它包含了一个套节字的信息
struct TIOCPContext
{
SOCKET s; // 套节字句柄
SOCKADDR_IN addrRemote; // 连接的远程地址
BOOL bClosing; // 套节字是否关闭
int nPostNum; // 此套节字上抛出的重叠操作的数量
BYTE* lpBufBegin; //拼包缓冲区头指针
BYTE* lpBufEnd; //拼包缓冲区尾指针
int arrayDataLen; //拼包缓冲目前长度
BYTE arrayDataBuf[USE_DATA_LONGTH]; //拼包缓冲区
CRITICAL_SECTION Lock; // 保护这个结构
};
bool CUserServer::SplitPacket(TIOCPContext *pContext, TPerIoData *pBuffer, char* outBuf)
{
int recvedCount = pBuffer->bufferLen; //收到的数据长度
//守卫pContext结构
CGuardLock(&pContext->Lock);
//如果缓冲区不够,说明包格式有问题,则丢弃数据
if(USE_DATA_LONGTH - pContext->arrayDataLen < recvedCount)
{
pContext->lpBufBegin = pContext->lpBufEnd = pContext->arrayDataBuf;
pContext->arrayDataLen = 0;
return false;
}
int lastLen;
//将接收的数据copy到整理缓冲区
if(pContext->lpBufEnd <= pContext->lpBufBegin) //假如尾指针在头指针前面
{
memcpy(pContext->lpBufEnd, pBuffer->buff, recvedCount);
}
else
{
lastLen = USE_DATA_LONGTH - (pContext->lpBufEnd - pContext- >arrayDataBuf); //尾指针到缓冲最后位置的大小
if(lastLen < recvedCount) //不能完全copy到最后
{
memcpy(pContext->lpBufEnd, pBuffer->buff, lastLen);
memcpy(pContext->arrayDataBuf, pBuffer- >buff + lastLen, recvedCount - lastLen);
}
else
{
memcpy(pContext->lpBufEnd, pBuffer->buff, recvedCount);
}
}
//更新尾指针
if(lastLen <= recvedCount)
{
pContext->lpBufEnd = pContext->arrayDataBuf + recvedCount - lastLen;
}
else
{
pContext->lpBufEnd += recvedCount;
}
//更新缓冲长度
pContext->arrayDataLen += recvedCount;
//根据包格式判断包是否完整,包格式 = type(4byte) + packetlen(4byte) + content
if(pContext->arrayDataLen < 8) //收到的包不完整
{
//返回,等下次再整理
return false;
}
char tmpBuf[10] = {'/0'};
lastLen = USE_DATA_LONGTH - (pContext->lpBufBegin - pContext->arrayDataBuf); //头指针到整理缓冲末尾的大小
if(lastLen < 8)
{
memcpy(tmpBuf, pContext->lpBufBegin, lastLen);
memcpy(tmpBuf + lastLen, pContext->arrayDataBuf, 8 - lastLen);
}
else
{
memcpy(tmpBuf, pContext->lpBufBegin, 8);
}
//得到包长度
int packLen = atoi(tmpBuf + 4);
if(packLen > MAX_PACKET_LEN || packLen < 0 )
{
//包长度有问题,则丢弃现在缓冲中的消息
pContext->lpBufBegin = pContext->lpBufEnd = NULL;
pContext->arrayDataLen = 0;
return false;
}
if(packLen > pContext->arrayDataLen - 8)
{
//包还不完整,等下次再整理
return false;
}
//现在将逻辑完整的包copy到out参数中去
if(lastLen < 8 + packLen)
{
memcpy(outBuf, pContext->lpBufBegin, lastLen);
memcpy(outBuf + lastLen, pContext->arrayDataBuf, 8 + packLen - lastLen);
}
else
{
memcpy(tmpBuf, pContext->lpBufBegin, 8 + packLen);
}
//更新头指针
if(lastLen <= 8 + packLen)
{
pContext->lpBufBegin = pContext->arrayDataBuf + 8 + packLen - lastLen;
}
else
{
pContext->lpBufBegin += (8 + packLen);
}
//更新长度
pContext->arrayDataLen -= 8 + packLen;
return true;
}