TCP协议是一种面向连接的可靠的传输层协议,它保证了数据的可靠传输,对于一些出错、超时丢包等问题TCP设计了超时重传机制,其基本原理:在发送一个数据之后,就开启一个定时器,若是在这个时间内没有收到发送数据的ACK确认报文,则对该报文进行重传,在达到一定次数还没有成功时放弃并发送一个复位信号。
1、超时重传时间
RTO(Retransmission TimeOut),影响超时重传机制协议效率的一个关键参数是重传超时时间RTO,RTO的值被设置过大过小都会对协议造成不利影响。如果RTO设置过大将会使发送端经过较长时间的等待才能发现报文段丢失,降低了连接数据传输的吞吐量;另一方面,若RTO过小,发送端尽管可以很快地检测出报文段的丢失,但也可能将一些延迟大的报文段误认为是丢失,造成不必要的重传,浪费了网络资源。
如果底层网络的传输特性是可预知的,那么重传机制的设计相对简单得多,可根据底层网络的传输时延的特性选择一个合适的RTO,使协议的性能得到优化。但是TCP的底层网络环境是一个完全异构的互联结构。在实现端到端的通信时,不同端点之间传输通路的性能可能存在着巨大的差异,而且同一个TCP连接在不同的时间段上,也会由于不同的网络状态具有不同的传输时延。
因次,TCP协议必须适应两个方面的时延差异:一个是达到不同目的端的时延的差异,另一个是统一连接上的传输时延随业务量负载的变化而出现的差异。为了处理这种底层网络传输特性的差异性和变化性,TCP的重传机制相对于其他协议显然也将更为复杂,其复杂性主要表现在对超时时间间隔的处理上。为此,TCP协议使用自适应算法(Adaptive Retransmission Algorithm)以适应互联网分组传输时延的变化。这种算法的基本要点是TCP监视每个连接的性能(即传输时延),由此每一个TCP连接推算出合适的RTO值,当连接时延性能变化时,TCP也能够相应地自动修改RTO的设定,以适应这种网络的变化。
RTO一般是根据RTT来设置的。若 RTO 小于 RTT,则会造成很多不必要的重传;若 RTO 远大于 RTT,则会降低整体网络利用率,RTO 是保证 TCP 性能的关键。并且不同连接的RTT不相同,同一个连接不同时间的 RTT 也不相同,所以 RTO 的设置一直都是研究热点。
2、连接往返时间
RTT(Round Trip Time),对一个连接而言,若能够了解端点间的传输往返时间,则可根据RTT来设置一合适的RTO。显然,在任何时刻连接的RTT都是随机的,无法事先预知。TCP通过测量来获得连接当前RTT的一个估计值,并以该RTT估计值为基准来设置当前的RTO。自适应重传算法的关键就在于对当前RTT的准确估计,以便适时调整RTO。
为了搜集足够的数据来精确地估算当前的RTT,TCP对每个报文都记录下发送出的时间和收到的确认时间。每一个(发送时间,确认时间)对就可以计算出一个RTT测量值的样本(Sample RTT)。TCP为每一个活动的连接都维护一个当前的RTT估计值。该值是对已经过去的一个时间段内该连接的RTT了两只的加权平均,并作为TCP对连接当前实际的RTT值的一种估计。RTT估计值将在发送报文段时被用于确定报文段的RTO。为了保证它能够比较准确地反应当前的网络状态,每当TCP通过测量获得了个新的RTT样本时,都将对RTT的估计值进行更新。不同的更新算法或参数可能获得不同的特性。
2.1、RTT的精确估值测量--Karn算法
如果在一个报文段中的数据被一次性地成功传输和确认,那么发送端可以准确得到该报文段传输的RTT样本。但若出现了重传,情况就会变得很复杂。例如,一个报文段发送后出现超时,TCP将在另一个报文段中重传。由于这两个报文段包含了同样的数据,发送方接收到确认信息时将无法分辨出确认信息到底是针对哪个报文段的,因为这两个报文段产生的确认信息可能是完全相同的,确认信息既可能是针对原始报文段的(这种情况可能是由于原报文段或确认在传输中被延迟造成的),也可能是对重传报文段的确认。这种现象称为确认二义性(Acknowledgement Ambiguiity)。确认的二义性将导致TCP无法准确地估算RTT。
为了避免确认二义性带来的问题,TCP采用了Karn算法来维护RTT的估计值。Karn算法规定,TCP只能利用没有确认二义性(既无重发、一次发送成功并得到确认的报文段)的RTT样本来对RTT的估计值进行调整。
3、重传
3.1、重传时发送数据的大小
数据在传输的时候不能只使用一个窗口协议,还需要有一个拥塞窗口来控制数据的流量,使得数据不会一下子都跑到网路中引起“拥塞”。拥塞窗口最初使用指数增长的速度来增加自身的窗口,直到发生超时重传,再进行一次微调。但是如何进行微调,拥塞避免算法和慢启动门限就是为此而生。
所谓的慢启动门限就是说,当拥塞窗口超过这个门限的时候,就使用拥塞避免算法,而在门限以内就采用慢启动算法。所以这个标准才叫做门限,通常,拥塞窗口记做cwnd,慢启动门限记做ssthresh。下面来看看拥塞避免和慢启动是怎么一起工作的。
3.2、算法概要
对一个给定的连接,初始化cwnd为1个报文段,ssthresh为65535个字节。TCP输出例程的输出不能超过cwnd和接收方通告窗口的大小。拥塞避免是发送方使用的流量控制,而通告窗口则是接收方进行的流量控制。前者是发送方感受到的网络拥塞的估 计,而后者则与接收方在该连接上的可用缓存大小有关。当拥塞发生时(超时或收到重复确认),ssthresh被设置为当前窗口大小的一半(cwnd 和接收方通告窗口大小的最小值,但最少为2个报文段)。此外,如果是超时引起了拥塞,则 cwnd被设置为1个报文段(这就是慢启动)。当新的数据被对方确认时,就增加cwnd,但增加的方法依赖于是否正在进行慢启动或拥塞避免。如果cwnd小于或等于ssthresh,则正在进行慢启动,否则正在进行拥塞避免。慢启动一直持续到回到当拥塞发生时所处位置的半时候才停止(因为记录了在步骤2 中制造麻烦的窗口大小的一半),然后转为执行拥塞避免。
3.3、快速重传和快速恢复算法
这是数据丢包的情况下给出的一种修补机制。一般来说,重传发生在超时之后,但是如果发送端接受到3个以上的重复ACK的情况下,就应该意识到,数据丢了,需要重新传递。这个机制是不需要等到重传定时器溢出的,所以叫做快速重传,它可以避免发送端因等待重传计时器的超时而空闲较长时间,以此增加网络吞吐量。而重新传递以后,因为走的不是慢启动而是拥塞避免算法,所以这又叫做快速恢复算法。流程如下:
当收到第3个重复的ACK时,将ssthresh设置为当前拥塞窗口cwnd的一半。重传丢失的 报文段。设置cwnd为ssthresh加上3倍的报文段大小。每次收到另一个重复的ACK时, cwnd增加1个报文段大小并发送1个分组(如果新的 cwnd允许发送)。当下一个确认新数据的ACK到达时,设置cwnd为ssthresh(在第1步中设置的值)。这个 ACK应该是在进行重传后的一个往返时间内对步骤1中重传的确认。另外,这个ACK也应该是对丢失的分组和收到的第1个重复的ACK之间的所有中间报文段 的确认。这一步采用的是拥塞避免。ICMP不会引起重新传递,TCP会坚持用自己的定时器,但是TCP会保留下ICMP的错误并且通知用户。
3.4、重新分组
TCP为了提高自己的效率,允许再重新传输的时候,只要传输包含重传数据报文的报文就可以,而不用只重传需要传输的报文。