TCP协议-滑动窗口、拆包和粘包

TCP、UDP都可以完成从一端往另一端发送数据,
只是UDP只是负责从发送端将数据发送出去就完了,不再管数据是否发送到接收端是否已经接收到了;
而TCP不仅负责发送数据,还确保数据是否送达,TCP是可靠的,而且它也是可以流控的,管理发送的速度,不能超过设备的承受能力。

TCP特性


1.可靠性
Reliability,TCP提供数据传的可靠性,确保接收端是否已接收到数据,如果超时没有收到接收端的确认,则会重新传输,

2.流控的
Data Flow Control,TCP还提供数据传输的流控,有一个缓冲区接管,不能传输任意大的数据量,这就由滑动窗口来实现这个特性。

滑动窗口Sliding Window

在四层协议中,TCP的下一层就是IP层,IP层协议是不可靠协议,所以需要TCP层自己的确认机制去确认数据是否已经传输到接收端了,在较早时候,使用send-wait-send的模式,也叫stop-wait模式,发送端在发送数据包package1后,会启动定时器检查package1是否收到ACK,定时器到期后,还没收到ACK则认为接收端没收到package1,发送端就会重传,这样降低了通信的效率,这机制叫positive acknowledgment with retransmission (PAR)。

假设每发送一个包都有一个id,发送端对每个包进行ACK,那么发送端可以一次性发送多个包,而不必等待接收端ACK之后再发下一个,同时接收端也会告诉发送端它能接收多少,这样发送端就不会发送过多的包了,当然也要保证包的顺序性,接收端可以先缓存提前到的数据,如果超过一定时间,等待的数据还没到达,则丢掉之前缓存的数据。

引入滑动窗口解决这个问题。

先看发送端的滑动窗口:

1.Sent and Acknowledged
已发送并且已确认的,数据已经传输给接收端并收到ACK,这部分数据已经在窗口之外,因为他们已经传输成功了,会从窗口中移除出去,实际的操作是窗口进行合拢,将需要传输的数据放进窗口范围。

2.Send But Not Yet Acknowledged
已发送但未确认的,数据已发送到接收端,但还没收到接收端的ACK,这部分属于窗口之内,因为这部分数据认为是没完成发送的。

3.Not Sent,Recipient Ready to Receive
没发送的但接收端有能力接收的,这部分数据已经加载到缓存中,也是属于窗口之内的,其实这部分数据是完全在接收端的告知接受范围能力之内的,所以传输端要尽快传输给接收端。

4.Not Sent,Recipient Not Ready to Receive
没发送的并且接收端没能力接收的,超出了接收端的接收范围了,所以这部分数据是不属于窗口之内的。
TCP协议-滑动窗口、拆包和粘包_第1张图片

发送窗口和可用窗口
所以对发送端来讲,属于可以发送的窗口之内的,就是2和3点了。2和3的范围又叫做发送窗口,发送窗口的范围是在TCP进行3次握手时,接收端告知发送端的,当然接收端在接收过程中也不断告知发送端发送窗口的大小: 3又叫可用窗口: 接收端告知这些数据也是在可接收范围之内的,但这些窗口范围还没发送数据出去的。
TCP协议-滑动窗口、拆包和粘包_第2张图片

接收端的滑动窗口:

接收端不需要像发送端那样等待ACK,所以它没有确认部分的数据。
1.Received and ACK Not Send to Process
接收到发送端的数据并且给发送端发过ACK信息,但没被上层应用接收的,所以这部分数据也是在窗口之内的。

2.Received Not ACK
接收到发送端的数据但没给发送端ACK信息的,这部分属于Deplay ACK的,所以这部分也是属于窗口的。

3.Not Received
有空余的空间,还没有接收到发送端的数据。
TCP协议-滑动窗口、拆包和粘包_第3张图片

滑动窗口原理
现在发送端有报文3、4要发送,接收端并不是每接收一个就回一个ACK,比如接收端先接收到4,但并不会马上对4回ACK,而在先放在缓存中等待3的空缺补上,如果3也送到了,再回一个ACK(又称累积ACK),如果3一直不送来,则会把已接收到的4也丢掉,这样发送端就执行重传机制。

1. 假设32~45 这些数据,是上层应用发送给TCP的,TCP将其分成四个报文段来发往接收端

2. seg1 32~34 seg3 35~36 seg3 37~41 seg4 42~45  这四个片段,依次发送出去,此时假设接收端之接收到了seg1 seg2 seg4

3. 此时接收端的行为是回复一个ACK包说明已经接收到了32~36的数据,并将seg4进行缓存(保证顺序,产生一个保存seg3 的空缺)

4. 发送端收到ACK之后,就会将32~36的数据包从发送并没有确认切到发送已经确认,踢出发送窗口,这个时候窗口向右移动

5. 假设接收端通告的Window Size仍然不变,此时窗口右移,产生一些新的空位,这些是接收端允许发送的范围

6. 对于丢失的seg3,如果超过一定时间,TCP就会重新传送(重传机制),重传成功会seg3 seg4一块被确认,不成功,seg4也将被丢弃

就是不断重复着上述的过程,随着窗口不断滑动,将整个数据流发送到接收端,实际上接收端的Window Size通告也是会变化的,接收端根据这个值来确定何时及发送多少数据,从对数据流进行流控。

我们可以通过机器的一些参数,看出TCP的发送、重传、连接失效机制。
cat /proc/sys/net/ipv4/tcp_mem
753600    1004800    1507200
cat /proc/sys/net/ipv4/tcp_wmem
4096    16384    4194304
cat /proc/sys/net/ipv4/tcp_rmem
4096    87380    4194304
cat /proc/sys/net/core/wmem_default
124928
cat /proc/sys/net/core/wmem_max
124928
cat /proc/sys/net/core/rmem_default
124928
cat /proc/sys/net/core/rmem_max
124928
cat /proc/sys/net/ipv4/tcp_retries2
15
cat /proc/sys/net/ipv4/tcp_keepalive_time
7200
cat /proc/sys/net/ipv4/tcp_keepalive_probes
9
cat /proc/sys/net/ipv4/tcp_keepalive_intvl
75
参照
http://www.360doc.com/content/14/0606/16/3300331_384326124.shtml


拆包和粘包

UCP是基于报文发送的,UDP报文的首部会有16bit来表现UDP数据的长度,所以不同的报文之间是可以区别隔离出来的,所以应用层接收传输层的报文时,不会存在拆包和粘包的问题;
而TCP是基于字节流传的,应用层和传输层之前数据交互是大小不等的数据块,但TCP对这些数据块只是一连串的数据流,它并不知道哪些数据块跟哪些数据块是该一起发,哪个数据块是应该单独一块的,因为TCP并没有像UDP那样首部有数据长度,所以TCP存在拆包和粘包的问题。

有了上面滑动窗口的知识,相信比较好理解为什么TCP会对数据块进行拆包和粘包的处理了,当然还有套接字(socket)缓存区可用大小的原因等等。

原因

1.要发送的数据包大于发送缓存区的可用空间时,数据被会拆包;
2.要发送的数据大于MSS(Maximum Segment Size最大报文段),TCP在发送前会对数据进行拆包;
TCP在三次握手建立连接过程中,会在SYN报文中使用MSS选项功能,协商交互双方能够接收的最大段长MSS值。
MSS是传输层TCP协议范畴内的概念,它是标识TCP能够承载的最大的应用数据段长度,因此,MSS=MTU-20字节TCP报头-20字节IP报头,那么在以太网环境下,MSS值一般就是1500-20-20=1460字节。
3.发送的数据小于缓存区,则TCP会将几次的数据一次性发送,会存在粘包;

拆包、粘包举例

比如发送端要往接收端发送2个数据包
1.收到2个数据包,没发送拆包和粘包情况;
2.收到1个数据包,TCP把2个数据包合成1个发送给接收端了,这样应用层不能处理合成1个的两个数据包,应用层不知道两个数据包之间的分隔在哪,所以很难处理,这是粘包问题;
3.收到2个数据包,但1个数据包产生了粘包(发送端的1个半数据包),另1个数据包产生了拆包(只有发送端中1个数据包的半个包),这样应用层也是很难处理粘包、拆包的;

解决

由于TCP是不知道应用层的数据包的分界的,所以我们应用层是决定不了传输层对数据包拆包还是粘包,不过应用层可以通应用层协议栈设计给数据包加分界标记,来处理最后接收到的数据,不管拆分还是粘包都可以处理好。

1.发送端给数据包增加首部,首部包含数据包中数据的长度,这样接收端的应用层接收数据后,根据首部中的长度就知道数据的实际长度了,可以很好处理数据了。
通常设计思路,比如第1个字段使用32int表示数据的长度,接着是数据内容。
2.设置数据包的长度为固定的长度,不够数据则以空格填补;
3.应用层在发送每个数据包时,给每个数据包加分界标记,比如回车换行,

在接收端接收的数据包个数总比发送端发送的个数少时,那是发送端的发送速度过快,导致接收端接收不过来,接收端会告知发送端的发送窗口为0,发送端新来数据只能暂存在发送缓存区中,当发送缓存区溢出后,则会出现丢包,可以增大发送缓存区来缓解,就是拥堵问题。

你可能感兴趣的:(network)