what

    所谓的粘包就是tcp的接收缓冲区由于各种原因导致多个消息没有边界的拼接在一起,无法区分上个包和下个包。我们也无法通过现有的函数接口将消息分开取出。备注:UDP不会

why

    为什么会出现这个问题呢,UDP为什么不会出现呢?这个要追溯到设计UDP和TCP的阶段。众所周知:TCP设计出来就是为了严谨的通讯,他是面向连接的,所以tcp需要3次握手,4次断连,并且传输中兼顾了重发等各种操作保证数据的稳定发送。为了数据的严谨,我们牺牲了很多的效率。主要原因如下:

    1.发送端引起粘包:

        由于上述的效率的大量牺牲,tcp在设计的时候,采用优化算法:Nagle算法,将数据合并成一个大包发送出去,可想而知,这个包到了接收端的接收缓冲区,内容就是多个包链接在一起的。UDP首先禁用了Nagle算法,所以不会出现粘包。

    2.接收端引起粘包:

        接收端的粘包形成的原因很多,第1种就是nagle算法引起的,包一到接收缓冲区就是粘包。很多人觉得禁用了Nagle算法就好了,但是如果我们的进程太繁忙,很可能出现无法及时取出数据,那么TCP粘包还是会出现,为什么?因为每一个到达的消息全部去掉TCP,IP的头,剩下的数据就是拼接在一起,那么我们还是分不开。

        那么为什么UDP为什么不会呢?

在UDP协议的接收端,采用了链式结构来记录每一个到达的UDP包,这样接收端应用程序一次recv只能从socket接收缓冲区中读出一个数据包。也就是说,发送端send了几次,接收端必须recv几次(无论recv时指定了多大的缓冲区)。

how

    那么如何避免呢?

TCP_NODELAY实现,但是可惜这个只能一定程度减少粘包的情况,原因:就是接收端不可能一直保持在兴奋状态一来就取,总会繁忙,并且网络延时,路由方式不同,导致两个包就是一起到的。所以这个方式不好。

        2.学UDP那样在socket缓冲区设计一个链式结构来实现?暂时不知道怎么搞呀。

        3.在接收端设置,通过该设置方案来实现分隔消息。方案有如下几种:

            3.1.通过设置结束符:我们可以在一个消息结尾设置一个结束符来实现消息分隔。但是会出现的问题:1.该分隔符一定不能出现在消息里,不然消息就雪崩式错位。2.这个只适用在简单的消息。

            3.2.通过设置长度:这里有的人或许会说,如果长度设置出错,那么缓冲区所有的数据全都错了。但是我们这些一般都是公共函数,不会去变的。

            3.2.先发送一个int的消息长度的消息,然后根据这个长度取数据。但是万一出现:体+长度+体。那我们可以通过体的首几个字节设置成非数字。如果4个字节可以转换成int,直接取数据体,但是直接取到数据体,那还得定位到下个表示长度的首地址在哪,感觉好麻烦。。。。以后再考虑吧

            3.3.TCP短连接方式:发一次就关闭,这种太脑残了。如果是多个客户端对应一个服务端还可以。否则每次通讯都要3次握手,4次断连,那可以离职了。

            3.4.学习把消息长度填写进,这样就可以避免了3.2的方法的那个体+长度+体的错误了。

            3.5.设置自己的协议。一般就是协议头(协议头肯定要包含长度,校验等)+协议体。协议体通过keyvalue的方式。一个avpcode对应一个值。为什么要这么设置呢,因为我们业务类型多的时候,可以通过这种方式实现正确的封包和解包。这个还有一个好处就是:如果你的包被人抓下来,然后串改,但是我们的协议只要一个avpcode对不上就可以认定这个包有问题。一定程度防止了被抓包串改的情况。(一般一个业务的avpcode都有几十个)