之前学习了滑动窗口,滑动窗口用来根据接收方的能接收数据的缓存大小来对发送方进行流量控制,从而减少网路负担,保证网络的正常运行。但是,在发送端和接收端之间,可能会存在很多中间设备,包括路由器、网关等,这些设备也具有一定的承载数据的上限,也会引起网络拥塞,造成数据的丢失,造成接收端接受数据的失序。为了解决这个问题,引入了拥塞窗口,即在发送端设置一个窗口结构,根据网络的拥塞情况,动态调整该窗口大小,发送端只能发送大小小于滑动窗口和拥塞窗口的数据,在发送端设置的这个窗口就是拥塞窗口。
一、超时重传
我们知道TCP提供的是面向连接的可靠数据传输。面向连接就是在交换数据前,在收发两端要建立连接。可靠主要体现在对于网络中出现的丢包、包乱序等情况,TCP发送端会根据接收端返回的情况,选择重传这些数据。在TCP中,主要通过两种方式来检测丢包或乱序的出现,第一种是发送端设置超时时间,第二种是接收端发送重复ACK包。
设置超时时间是指,TCP发送一个报文段时,设置该报文段的超时时间,如果接收端接收到报文段,会给发送端发送一个ACK确认报文,如果发送端在超时时间前未收到对应的ACK报文,发送端则会选择重发丢失的报文,直到发送端收到非重传数据为止。设置超时时间的核心问题是如何确定RTO(Retransmission Time Out, 重传超时时间),有很多关于这个问题的研究和成果。RTO是动态的,会根据网路拥塞情况动态变化。最核心的是,基本上所有的方法都是通过连接的RTT(Round Trip Time, 往返时间)确定RTO。早期的方法是通过每次得到的RTT样本值重新计算新的TRTO值。公式如下。其中SRTT为每次的迭代更新的RTT,RTT为样本RTT测量值。ubound、lbound、β为常量。
早期的经典方法没有考虑到RTT的变化情况,RTT变化情况更能反映当前的拥塞情况,所以Jacobson提出了标准方法。具体公式这里不想讨论。同时对于我们如何获取样本RTT,也有一些值得探讨的问题。我们一般是通过在发送的报文中设置某些标识,在接收端收到并返回ACK时也会带上这些标识,我们就可测量这段时间为RTT。但是如果在测量RTT时发生超时重传,如果第一个包发生超时,就会重传,接着收到了一个ACK确认报文,但这个确认是对那一次发送的确认呢?可能第一次发送的报文,由于网络拥堵,接收端只是延时收到了,但触发了超时时间,所以这个ACK有可能就是对第一个报文的确认,这就产生了二义性。也就产生了Karn算法。Karn算法包含两个部分,一是发生超时重传时,接受到重传数据的确认信息时不能更新RTT估计值,这样就忽略了二义性的问题。二是采用一个退避指数,每当重传的报文计时器超时时,就让退避指数加倍,即让下一次重发时间加倍,直到接受到非重传数据。
下面来看基于接收端发给发送端的报文,判断网络的拥塞,是否出现丢包或包乱序的问题。一般情况是,当接收端发现数据丢失或数据乱序时,接收端选择发送重复ACK确认报文,向发送端报告数据丢失或乱序。一般来说,小于三个ACK重复报文代表数据乱序,大于等于三个重复ACK代表数据丢失。当发送端观察到这种情况后,不管超时计时器是否超时,都会选择重发丢失或乱序的数据。
二、慢启动和拥塞控制
前面了解了超时重发后,我们开始讨论慢启动和拥塞控制。慢启动顾名思义,是为了进行流量控制,发送端控制发送数据的大小的动作。初始时,发送端发送一个报文段,等收到接收端对该报文段的确认ACK后,发送端就可以发送2个报文段,当收到对这两个报文段的确认ACK后,发送端就可以发送4个报文段,一直按照指数增长下去。这种TCP中使用的发送数据的方式为慢启动(Slow Start)。一般在TCP中,慢启动和拥塞控制同时使用。
下面介绍拥塞控制,拥塞控制也就是在发送端设置一个窗口结构,窗口大小为cwnd,其根据网络的拥塞情况动态变化,也就是根据连接的超时重发情况动态变化。发送端只能发送小于或等于拥塞窗口和滑动窗口大小的数据。初始时cwnd为1,ssthreshold(Start Slow Threshold)为65535。当cwnd <= ssthreshold时,执行慢启动;当cwnd > ssthreshold后,执行拥塞控制。慢启动前面说了可以传输的数据量以指数形式增长。拥塞控制是cwnd线性增长,即要么以1/cwnd,要么以1增长。
1.当出现因超时引起的重传时,执行:
1.ssthreshold = cwnd / 2
2.cwnd = 1
3.进入慢启动
2.当出现发送端接收到三个重复的ACK后发生的重传,执行:
1.cwnd = cwnd / 2
2.ssthreshold = cwnd
3.进入快速恢复算法
快速恢复算法:
1.前面已经更新了cwnd和ssthreshold
2.随后设置cwnd = cwnd + 3 * MSS
3.重传数据
4.再每收到一个重复ACK,cwnd = cwnd + 1
5.直到没收到重复ACK,设置cwnd = ssthreshold
原文地址:https://www.cnblogs.com/glsy/p/8605848.html