TCP 拆包 ,粘包

  1. TCP 协议(只有TCP有粘包现象,UDP永远不会粘包)
    TCP 是一个面向字节流的协议,它是性质是流式的,所以它并没有分段。就像水流一样,你没法知道什么时候开始,什么时候结束。
    所以他会根据当前的套接字缓冲区的情况进行拆包或是粘包。
    下图展示了一个 TCP 协议传输的过程:
    在这里插入图片描述

  2. 发送端的字节流都会先传入缓冲区,再通过网络传入到接收端的缓冲区中,最终由接收端获取。
    当我们发送两个完整包到接收端的时候:正常情况会接收到两个完整的报文。
    在这里插入图片描述

    1>.但也有以下的情况:接收到的是一个报文,它是由发送的两个报文组成的,这样对于应用程序来说就很难处理了(这样称为粘包)。
    TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。
    在这里插入图片描述

    2>. 还有可能出现上面这样的虽然收到了两个包,但是里面的内容却是互相包含,对于应用来说依然无法解析(拆包)。
    在这里插入图片描述

  3. 为什么出现粘包现象:
    原因:接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。
    此外,发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。


	 1. TCP(transport controlprotocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。
	 收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,
	 更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一
	 个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。即面向
	 流的通信是无消息保护边界的。
	 2. UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。
	  不会使用块的合并优化算法,由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓
	  冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源
	 地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是
	 有消息保护边界的。
	 3. tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处
	  理机制,防止程序卡住,而udp是基于数据报的,即便是你输入的是空内容(直接回车),那也
	 不是空消息,udp协议会帮你封装上消息头。

  1. tcp会发生粘包的两种情况如下:
    1、发送端多次send间隔较短,并且数据量较小,tcp会通过Nagls算法,封装成一个包,发送到接收端,接收端不知道这个包由几部分组成,所以就会产生粘包。
    2、数据量发送的大,接收端接收的小,再接一次,还会出现上次没有接收完成的数据。就会出现粘包。
    示例1: 发送端多次send间隔较短,并且数据量较小,tcp会通过Nagls算法,封装成一个包,发送到接收端,接收端不知道这个包由几部分组成,所以就会产生粘包。
    示例2:指定接收字节数,相当于服务端知道接收长度,就不会出现粘包现象
    示例3:数据量发送的大,接收端接收的小,再接一次,还会出现上次没有接收完成的数据。就会出现粘包。

  2. 如何处理粘包现象
    (1)发送方
    对于发送方造成的粘包现象,我们可以通过关闭Nagle算法来解决,使用TCP_NODELAY选项来关闭Nagle算法。
    (2)接收方
    遗憾的是TCP并没有处理接收方粘包现象的机制,我们只能在应用层进行处理。
    (3)应用层处理
     应用层的处理简单易行!并且不仅可以解决接收方造成的粘包问题,还能解决发送方造成的粘包问题。
    解决方法就是循环处理:应用程序在处理从缓存读来的分组时,读完一条数据时,就应该循环读下一条数据,直到所有的数据都被处理;但是如何判断每条数据的长度呢?
    两种途径:
    1)格式化数据:每条数据有固定的格式(开始符、结束符),这种方法简单易行,但选择开始符和结束符的时候一定要注意每条数据的内部一定不能出现开始符或结束符;
    2)发送长度:发送每条数据的时候,将数据的长度一并发送,比如可以选择每条数据的前4位是数据的长度,应用层处理时可以根据长度来判断每条数据的开始和结束。

  • 对于这样的问题只能通过上层的应用来解决,常见的方式有:
    - 在报文末尾增加换行符表明一条完整的消息,这样在接收端可以根据这个换行符来判断消息是否完整。
    - 将消息分为消息头、消息体。可以在消息头中声明消息的长度,根据这个长度来获取报文(比如 808 协议)。
    - 规定好报文长度,不足的空位补齐,取的时候按照长度截取即可。

  • 法一:解决思路:就是提前发一个头过去,告诉客户端需要接收的长度(分两步:1、发送发度 2、再次发送数据)-----------程序的运行速度远快于网络传输速度,所以在发送一段字节前,先用send去发送该字节流长度,这种方式会放大网络延迟带来的性能损耗。

  • 法二:节省网络传输版本: 为字节流加上自定义固定长度报头,报头中包含字节流长度,然后一次send到对端,对端在接收时,先从缓存中取出定长的报头,然后再取真实数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值