TCP粘包问题的分析和解决方法

1、TCP粘包的问题分析
在socket网络程序中,TCP是面向连接的,所以在TCP的socket编程中,收发两端(client和server)都要有成对的socket。因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小、数据量小的数据,合并成一个大的数据块,然后进行封包,就导致接收端难以分辨出来,所以必须提供科学的拆包机制。
对于UDP,它是非面向连接的,不会使用块的合并优化算法。实际上,目前大多认为由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),对于接受端来说,就容易进行区分处理了。所以UDP不会出现粘包问题。

2、TCP协议简介
  TCP是一个面向连接的传输层协议,虽然TCP不属于ISO制定的协议集,但由于其在商业界和工业界的成功应用,它已经成为事实上的网络标准,广泛应用于各种网络主机间的通信。
  
  作为一个面向连接的传输层协议,TCP的目标是为用户提供可靠的端到端连接,保证信息有序无误的传输。它不仅可以提供基本的数据传输功能,还为了保证可靠性采用了数据编号、校验和计算、数据确认等措施。对每个传送的字节都进行编号,并请求接收方回传确认信息(ACK)。如果发送方在规定的时间内没有收到数据确认,就重传该数据。
  TCP为用户提供了高可靠性的网络传输服务,但可靠性保障措施也影响了传输效率。所以在实际工程应用中,只有关键数据的传输才采用TCP,而普通数据的传输一般采用高效率的UDP。
  
3、粘包问题的分析与解决方法

  • TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。出现粘包现象的原因是多方面的,它既可能由发送方造成,也可能由接收方造成。
  • 什么时候需要考虑粘包问题:
    1)如果利用TCP每次发送数据,就与对方建立连接,然后双方发送完一段数据后,就关闭连接,这样就不会出现粘包问题
    2)如果发送数据无结构,如文件传输,这样发送方只管发送,接收方只管接收存储,也不用考虑粘包
    3)如果双方建立连接,需要在连接后一段时间内发送不同数据结构,如果连接后,有好几种结构,就会容易出现粘包.
  • 粘包出现原因:
    简单地说,在流传输中出现,UDP不会出现粘包,因为它有消息边界(后文补充)
    1)发送端需要等缓冲区满才发送出去,造成粘包
    2)接收方不及时接收缓冲区的包,造成多个包接收
  • 为避免粘包现象,可采取一下几种措施:
    1)对于发送方引起的粘包现象,用户可通过编程设置来避免,TCP提供了强制数据立即传送的操作指令push,TCP软件收到该操作指令后,就立即将本段数据发送出去,而不必等待发送缓冲区满;
    2)对于接收方引起的粘包,则可通过优化程序设计、精简接收进程工作量、提高接收进程优先级等措施,使其及时接收数据,从而尽量避免出现粘包现象;
    3)由接收方控制,将一包数据按结构字段,人为控制分多次接收,然后合并,通过这种手段来避免粘包。
    当然万物都不会绝对的好,这三种方法都有其不足之处.
    1)第一种编程设置方法虽然可以避免发送方引起的粘包,但它关闭了优化算法,降低了网络发送效率,影响应用程序的性能,一般不建议使用。
    2)第二种方法只能减少出现粘包的可能性,但并不能完全避免粘包,当发送频率较高时,或由于网络突发可能使某个时间段数据包到达接收方较快,接收方还是有可能来不及接收,从而导致粘包。
    3)第三种方法虽然避免了粘包,但应用程序的效率较低,对实时应用的场合不适合。

4、结论
1)TCP为了保证可靠传输,尽量减少额外开销(每次发包都要验证),因此采用了流式传输,面向流的传输,相对于面向消息的传输,可以减少发送包的数量,从而减少了额外开销。但是,对于数据传输频繁的程序来讲,使用TCP可能会容易粘包。当然,对接收端的程序来讲,如果机器负荷很重,也会在接收缓冲里粘包。这样,就需要接收端额外拆包,增加了工作量。因此,这个特别适合的是数据要求可靠传输,但是不需要太频繁传输的场合(两次操作间隔100ms,具体是由TCP等待发送间隔决定的,取决于内核中的socket的写法)

(2)UDP,由于面向的是消息传输,它把所有接收到的消息都挂接到缓冲区的接受队列中,因此,它对于数据的提取分离就更加方便,但是,它没有粘包机制,因此,当发送数据量较小的时候,就会发生数据包有效载荷较小的情况,也会增加多次发送的系统发送开销(系统调用,写硬件等)和接收开销。因此,应该最好设置一个比较合适的数据包的包长,来进行UDP数据的发送。(UDP最大载荷为1472,因此最好能每次传输接近这个数的数据量,这特别适合于视频,音频等大块数据的发送,同时,通过减少握手来保证流媒体的实时性)