基于TCP网络传输的应用程序有时会出现粘包现象(即发送方发送的若干包数据到接收方接收时粘成一包)
TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。
出现粘包现象的原因是多方面的,它既可能由发送方造成,也可能由接收方造成。发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一包数据。若连续几次发送的数据都很少,通常TCP会根据优化算法把这些数据合成一包后一次发送出去,这样接收方就收到了粘包数据。接收方引起的粘包是由于接收方用户进程不及时接收数据,从而导致粘包现象。这是因为接收方先把收到的数据放在系统接收缓冲区,用户进程从该缓冲区取数据,若下一包数据到达时前一包数据尚未被用户进程取走,则下一包数据放到系统接收缓冲区时就接到前一包数据之后,而用户进程根据预先设定的缓冲区大小从系统接收缓冲区取数据,这样就一次取到了多包数据(图1所示)。
图1
图2
图3
粘包情况有两种,一种是粘在一起的包都是完整的数据包(图1、图2所示),另一种情况是粘在一起的包有不完整的包(图3所示),此处假设用户接收缓冲区长度为m个字节。
不是所有的粘包现象都需要处理,若传输的数据为不带结构的连续流数据(如文件传输),则不必把粘连的包分开(简称分包)。但在实际工程应用中,传输的数据一般为带结构的数据,这时就需要做分包处理。
实际解决:
接受端
int iRecvSize = PackteSize + 10;
int iRet;
int idx = 0;
while (iRecvSize > 0)
{
iRet = recv(AcceptSocket, recvbuf+idx, iRecvSize, 0);
if (iRet > 0)
{
idx += iRet;
iRecvSize -= iRet;
}
else if (iRet == 0)
{
break;
}
else if ( iRet == SOCKET_ERROR)
{
break;
}
}
发送端
int iSendSize = PacketSize + 10;
int iSent;
int idx = 0;
while (iSendSize > 0)
{
iSent = send(m_socket,sendbuffer+idx,iSendSize,0);
if (iSent > 0)
{
idx += iSent;
iSendSize -= iSent;
}
else if (iSent == 0)
{
break;
}
else if (iSent == SOCKET_ERROR)
{
wprintf(L"send failed with error: %d\n", WSAGetLastError());
//closesocket(m_socket);
//WSACleanup();
break;
}
}
这种加强版的send,和 recv可以处理这种粘包而产生的传输而导致的数据错误。