1. TCP提供可靠连接的途径是通过接受方确认发送方的数据实现的,数据和确认都可能丢失,这就需要及时发现数据或者确认丢失而进行重传。重传最重要的是要确定超时间隔和重传频率。
2. 对于每个连接,TCP管理4个定时器来完成数据的传输:
重传定时器
坚持定时器(persist):使窗口大小保持不断流动;
保活定时器(keepalive):检测空闲连接的另一端何时崩溃;
2MSL定时器:测量一个连接处于TIME_WAIT状态的时间。
3. 确定超时重传的重要一步是测量RTT的值,根据RTT的值依据算法得到RTO的值,TCP在收到接受方回应时更新计算当时的RTT。当超时重传发生时,在重传数据的确认最后到达之前不能重新计算RTT。
4. 源于伯克利的TCP实现在任何时候对每个连接仅测量一次RTT值,即在发送一个报文时,如果给定连接的一个定时器已经使用,就不对该报文段计时。计时使用500ms的定时器;
5. 快速重传:源于伯克利的TCP实现在收到连续三个相同的确认时,就重传确认序号起的下一个报文段,而不用等待RTO超时;快速恢复,当超时重传发生时,执行快速重传和拥塞避免而不是慢启动。
6. 拥塞避免算法:
a. 对于给定连接,初始化拥塞窗口(cwnd)为1个报文段,慢启动门限(sstresh)为65535个字节;
b. TCP输出不能超过cwnd和接收方通告的窗口大小;
c. 当超时或者收到重复确认时,sstreth被设置为当前窗口的一半,进入拥塞避免;如果是超时,还要将cwnd设置为1个报文段,进入慢启动;
d. 收到新的确认时,根据当时是拥塞避免还是慢启动增加相应的cwnd;拥塞避免cwnd每次增加cwnd/1,使得在一个往返时间内cwnd最多增加1;慢启动cwnd每次增加1,是一种指数增长。
7. 对ICMP差错处理:源站抑制的错误进行慢启动;网络或者主机不可达被忽略,一直到超时后断开连接;
8. 重新分组:重传分组可能和后续分组放在一个报文段中发送到接收方;
9. 没有做特殊设置的话,超时重传一直到关闭连接的时间为9分钟,超时重传的时间使用指数退避
超时重传情况分析,看下图
RTT和RTo概念详解
拥塞避免算法
拥塞避免算法和慢启动算法需要对每个连接维持两个变量:一个拥塞窗口cwnd和一个慢启动门限ssthresh。这样得到的算法的工作过程如下:
1) 对一个给定的连接,初始化cwnd为1个报文段,ssthresh为65535个字节。
2) TCP输出例程的输出不能超过cwnd和接收方通告窗口的大小。拥塞避免是发送方使用的流量控制,而通告窗口则是接收方进行的流量控制。前者是发送方感受到的网络拥塞的估计,而后者则与接收方在该连接上的可用缓存大小有关。
3) 当拥塞发生时(超时或收到重复确认),ssthresh被设置为当前窗口大小的一半( cwnd 和接收方通告窗口大小的最小值,但最少为2个报文段)。此外,如果是超时引起了拥塞,则cwnd被设置为1个报文段(这就是慢启动)。
4) 当新的数据被对方确认时,就增加cwnd ,但增加的方法依赖于我们是否正在进行慢启动或拥塞避免。如果cwnd小于或等于ssthresh ,则正在进行慢启动,否则正在进行拥塞避免。慢启动一直持续到我们回到当拥塞发生时所处位置的半时候才停止(因为我们记录了在步骤2中给我们制造麻烦的窗口大小的一半),然后转为执行拥塞避免。
linux 如何计算RTO值
这里说的是RHEL5.4的2.6.18内核,RFC-2988实现参考net/ipv4/tcp_input.c中的 tcp_rtt_estimator和tcp_set_rto。可以看到,在Linux中alpha=1/8,RTO最小为TCP_RTO_MIN。因为 我们的系统中RTT总是很小,所以RTO取值总是能够取到TCP_RTO_MIN。
如下:
123#define TCP_RTO_MAX ((unsigned)(120*HZ))
124#define TCP_RTO_MIN ((unsigned)(HZ/5))
要检查系统上HZ的值是什么,就执行命令
cat /boot/config-`uname -r` | grep '^CONFIG_HZ='
我这里是1000,因此我这边RTO为200ms,在这里需要强调一下,RTO重传间隔是指数增加的,在我这里是200*2ms,200*4ms,200*8ms。。。递增的;
还有要强调一下,重传次数是根据内核参数定制的,如下:
[root@ ~]# sysctl -a | grep tcp_retries1 net.ipv4.tcp_retries1 = 3 [root@~]# sysctl -a | grep tcp_synack_retries net.ipv4.tcp_synack_retries = 5
下面是一篇详细描述超时重传的文章