在陈述粘包之前先说说TCP和UDP的传输特点:TCP是流模式,想象河流中的水流,它是持续的,没有边界的;再说UDP,UDP是数据报模式,想象你的收发快递的包裹,它就是你要传输的数据。它的数据传输有边界,它是按照一包一包来发送和接收的。
那么接下来,我们要说的是一下三个问题:
问题1:什么是粘包?
粘包就是在接收端看来两个或者多个数据包粘成了一包,包与包之间首尾相连。
通过介绍TCP和UDP传输数据的特点,我们知道,因为TCP的无边界和UDP的有边界,决定了粘包的出现发生在TCP传输中。
问题2:为什么会出现粘包?
出现粘包的原因有多种,可能出现在发送端,也可能出现在接收端。
发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一包数据。若连续几次发送的数据都很少,通常TCP会根据优化算法把这些数据合成一包后一次发送出去,这样接收方就收到了粘包数据。接收方引起的粘包是由于接收方用户进程不及时接收数据,从而导致粘包现象。这是因为接收方先把收到的数据放在系统接收缓冲区,用户进程从该缓冲区取数据,若下一包数据到达时前一包数据尚未被用户进程取走,则下一包数据放到系统接收缓冲区时就接到前一包数据之后,而用户进程根据预先设定的缓冲区大小从系统接收缓冲区取数据,这样就一次取到了多包数据。
问题3:如何解决粘包?
一是对于发送方引起的粘包现象,用户可通过编程设置来避免,TCP提供了强制数据立即传送的操作指令push,TCP软件收到该操作指令后,就立即将本段数据发送出去,而不必等待发送缓冲区满;二是对于接收方引起的粘包,则可通过优化程序设计、精简接收进程工作量、提高接收进程优先级等措施,使其及时接收数据,从而尽量避免出现粘包现象;三是由接收方控制,将一包数据按结构字段,人为控制分多次接收,然后合并,通过这种手段来避免粘包。
以上提到的三种措施,都有其不足之处。第一种编程设置方法虽然可以避免发送方引起的粘包,但它关闭了优化算法,降低了网络发送效率,影响应用程序的性能,一般不建议使用。第二种方法只能减少出现粘包的可能性,但并不能完全避免粘包,当发送频率较高时,或由于网络突发可能使某个时间段数据包到达接收方较快,接收方还是有可能来不及接收,从而导致粘包。第三种方法虽然避免了粘包,但应用程序的效率较低,对实时应用的场合不适合。
接收端:
int recvSize = packetSize + 10;
int index = 0;
while (recvSize > 0)
{
int ret =recv(connfd, recvbuf+index, recvSize, 0);
if (ret >0)
{
index +=ret;
recvSize-= ret;
}
else if (ret== 0 )
{
break;
}
else if (ret== SOCKET_ERROR)
{
break;
}
}
发送端:
int sendSize = packetSize + 10;
int index = 0;
while (sendSize > 0)
{
int ret =send(clifd, sendbuffer+index, sendSize, 0);
if (ret >0)
{
index +=ret;
sendSize-= ret;
}
else if (ret== 0)
{
break;
}
else if (ret== SOCKET_ERROR)
{
break;
}
}