3. 网络优化
3.1 滑动窗口
TCP使用滑动窗口协议来进行流量控制。特别需要注意的是,滑动窗口是一个抽象的概念,它是针对每一个TCP连接的,而且是有方向的,一个TCP连接应该有两个滑动窗口,每个数据传输方向上有一个,而不是针对连接的每一端的。
窗口左边沿向右边滑动叫做窗口合拢,表示发送方发送了数据或者接收到了确认;窗口右边沿向右边滑动叫做窗口的张开,表示数据已经被用户空间进程接收并且释放了缓存;窗口左边沿向左移动则表明此ACK是重复ACK,应该丢弃;窗口右边沿向左移动叫做窗口收缩,一般不会有人这样做。
当左边沿和右边沿重合的时候表明窗口大小是0,此时发送方不应该在发送数据了,因为接收方的接收缓冲区已满,用户进程还没以接收。当用户进程接收完成后,接收方应该发送一个ACK,表明此时的接收窗口已经恢复,此ACK的序号同前一个win为0的ACK相同。
同样,在实现中,发送方不必发送一个全窗口的数据,但是它当然可以这样做。ACK总是将窗口向右边滑动,窗口的大小可以减小,接收方在发送ACK之前不必等待窗口被填满(即变为0),很多实现是收到两个数据报文段后立刻发送ACK。
-
原理:一个窗口可以同时发送多少数据,当有ACK返回,确定有多少数据送到了,就再发送多少数据,从而达到了提高效率的目的。 规当传输速度足够快时,那么窗口就变成了滑动,因此叫做窗口滑动。
-
ACK包丢失:前几个ACK都丢包了,那也毫无影响,只要最后一个ACK送达了,就为正常,因为ACK中的确认序号就是告诉他,确实序号之前的字节都收到了,因此只要最后一个ACK没丢就可以。如果是最后一个包丢了,或者是最后几个都丢了,那就会触发超时重传机制。
-
数据包丢失:当有数据段丢了,那么 后续所有应答报文中的确认序号就会一直是固定值(即索要1001及以后的数据), 不会因为后续数据送达而改变,会一直保持到丢失送达。当俩奴性收到相同的应答时会触发快速应答,发送丢失数据,不等到超时。
-
伪超时和重传:过早的判断了超时时间,导致发送方触发重传,RTT(一个连接的往返时间,即数据发送时刻到接收到确认的时刻的差值)增长 超过RTO(重传超时时间,即从数据发送时刻算起,超过这个时间便执行重传, RTO协议实现值最小1s, 最好的结果是 RTO 略大于 RTT)
-
包失序:ip层的包没有按顺序传输,严重失序时,接收方误以为包丢失,通知发送端重传,需要设置合理的重传阀值解决
-
包重复:重传包含一个数据包,以及两个副本,多次重复会导致收到过多重复包,从而生成重复的ack,容易触发伪快速重传,使用sack避免
-
SACK:基于Fast Retransmit的算法上的优化.当出现包失序、网络丢包导致的接收方数据序队列出现空洞,sack选项可以提供确认信息(描述乱序、空洞),帮助有效的重传。
-
Zero Window(ZWP):发送端可以发送数据动态修改“滑动窗口”的大小,注意其值是可以为0的!那这样是否意味着发送端就不发数据了?确实,接收端都已经表示自己无力接收了,因此不会再发,类似“Window Closed”。可是过了一会儿,接收端如何通知发送端窗口可用了呢?解决这个问题,TCP使用了Zero Window Probe技术,缩写为ZWP。即发送端在窗口变成0后,会发ZWP的包给接收端,让接收端来ack他的Window尺寸,一般这个值会设置成3次,第次大约30-60秒(不同的实现可能会不一样)。如果3次过后还是0的话,有的TCP实现就会发RST把链接断了。
3.2 阻塞控制
-
慢启动
最初的TCP在连接建立成功后会向网络中发送大量的数据包,这样很容易导致网络中路由器缓存空间耗尽,从而发生拥塞。因此新建立的连接不能够一开始就大量发送数据包,而只能根据网络情况逐步增加每次发送的数据量,以避免上述现象的发生。
-
连接建好的开始先初始化cwnd = 1,表明可以传一个MSS大小的数据。
-
每当收到一个ACK,cwnd++; 呈线性上升
-
每当过了一个RTT,cwnd = cwnd*2; 呈指数让升
-
还有一个ssthresh(slow start threshold),是一个上限,当cwnd >= ssthresh时,就会进入“拥塞避免算法”
-
-
拥塞避免算法(Congestion Avoidance)
-
从慢启动可以看到,cwnd可以很快的增长上来,从而最大程度利用网络带宽资源,但是cwnd不能一直这样无限增长下去,一定需要某个限制。TCP使用了一个叫慢启动门限(ssthresh)的变量,当cwnd超过该值后,慢启动过程结束,进入拥塞避免阶段。对于大多数TCP实现来说,ssthresh的值是65536(同样以字节计算)。拥塞避免的主要思想是加法增大,也就是cwnd的值不再指数级往上升,开始加法增加。此时当窗口中所有的报文段都被确认时,cwnd的大小加1,cwnd的值就随着RTT开始线性增加,这样就可以避免增长过快导致网络拥塞,慢慢的增加调整到网络的最佳值。
这样就可以避免增长过快导致网络拥塞,慢慢的增加调整到网络的最佳值。很明显,是一个线性上升的算法。
-
收到一个ACK时,cwnd = cwnd + 1/cwnd
-
当每过一个RTT时,cwnd = cwnd + 1
-
-
-
超时重传
超时重传是指发送数据后设置一个时间,超过这个时间没有收到ACK应答就重传(无论是发送的数据包丢失还是ACK应答丢失)。
-
重传计时器:Retransmission Timer A发报文时创建计时器,计时器到期内收到回报文ACK,就撤销计时器
-
-
快速重传
是当收到三个相同的 ACK 报文时,会在定时器过期之前,重传丢失的报文段。
-
快速恢复算法(Fast Recovery)
-
当收到3个重复ACK时,把ssthresh设置为cwnd的一半,把cwnd设置为ssthresh的值加3,然后重传丢失的报文段,加3的原因是因为收到3个重复的ACK,表明有3个“老”的数据包离开了网络。cwnd = sshthresh + 3 * MSS (3的意思是确认有3个数据包被收到了)
-
在收到重复的ACK时,拥塞窗口增加1。如果再收到 duplicated Acks,那么cwnd = cwnd +1
-
当收到新的数据包的ACK时,把cwnd设置为第一步中的ssthresh的值。原因是因为该ACK确认了新的数据,说明从重复ACK时的数据都已收到,该恢复过程已经结束,可以回到恢复之前的状态了,也即再次进入拥塞避免状态。如果收到了新的Ack,那么,cwnd = sshthresh ,然后就进入了拥塞避免的算法了。
-