前言
关于TCP连接的状态变迁,还有TCP有重传机制,这里仅对重传机制在系统上的具体表现进行测试,因为之前仅知道在系统上,涉及到重传的内核参数有如下四个,但个中的细节还是不得而知
名称
|
默认值 | 建议值 |
描述
|
tcp_syn_retries | 5 | 2 | 对于一个新建连接,内核要发送多少个 SYN连接请求才决定放弃。不应该大于255,默认值是5,对应于180秒左右时间。。(对于大负载而物理通信良好的网络而言,这个值偏高,可修改为2.这个值仅仅是针对对外的连接,对进来的连接,是由tcp_retries1决定的) |
tcp_retries1 | 3 | 3 | 放弃回应一个TCP连接请求前﹐需要进行多少次重试。RFC 规定最低的数值是3 |
tcp_synack_retries | 5 | 2 | 对于远端的连接请求SYN,内核会发送SYN +ACK数据报,以确认收到上一个 SYN连接请求包。这是所谓的三次握手( threeway handshake)机制的第二个步骤。这里决定内核在放弃连接之前所送出的 SYN+ACK 数目。不应该大于255,默认值是5,对应于180秒左右时间。 |
tcp_retries2 | 15 | 5 | 在丢弃激活(已建立通讯状况)的TCP连接之前﹐需要进行多少次重试。默认值为15,根据RTO的值来决定,相当于13-30分钟(RFC1122规定,必须大于100秒). |
这次测试主要是针对参数tcp_retries2,查询资料得知重传间隔RTO(Retransmission Timeout)一般是动态计算出来的(公式在不同的RFC中有所不同),且会采取指数回退(binary exponential backoff)的方式进行递增,也就是说假如第一次等待的时间是1s,那么后续依次是等待2s、4s、8s
那在linux上的体现是如何的呢,第一次等待超时时间是多少,默认重传总体耗时又是多久
测试环境
主机A:10.18.5.132 redis-cli
主机B:10.17.1.54 redis-server
|
测试流程
Step1
主机A使用redis-cli连接上主机B的redis-server,通过3次握手后建立起TCP连接
[root@master tcpTest]# netstat -atnlp|grep 10.17.1.54
tcp 0 0 10.18.5.133:40758 10.17.1.54:9011 ESTABLISHED 11004/./redis-cli
|
[root@localhost tcpTest]# netstat -antlp|grep 10.18.5.133
tcp 0 0 10.17.1.54:9011 10.18.5.133:40758 ESTABLISHED 12382/redis-serverli
|
Step2
主机A通过redis-cli发送PING请求,TCP连接状态正常

Step3
开启FW,禁止主机A发数据到主机B,两端TCP连接依然是ESTABLISHED状态(同Step1记录的结果)
Step4
再次从主机A发送PING请求,记录被FW丢弃的数据包


根据上面的抓包结果,可以分析出每次重传的等待时间了,第一次的等待时间大概为200ms,最大的等待时间为120s,指数增长到了120就封顶了,看来应该是系统作了限制
发包 | 等待次数 |
RTO(s)
|
发PING | ||
等待1 | 0.200365 | |
重传1 | ||
等待2 | 0.402045 | |
重传2 | ||
等待3 | 0.803957 | |
重传3 | ||
等待4 | 1.607988 | |
重传4 | ||
等待5 | 3.216799 | |
重传5 | ||
等待6 | 6.431175 | |
重传6 | ||
等待7 | 12.86406 | |
重传7 | ||
等待8 | 25.72797 | |
重传8 | ||
等待9 | 51.45601 | |
重传9 | ||
等待10 | 102.9129 | |
重传10 | ||
等待11 | 120 | |
重传11 | ||
等待12 | 120 | |
重传12 | ||
等待13 | 120 | |
重传13 | ||
等待14 | 120 | |
重传14 | ||
等待15 | 120 | |
重传15 | ||
等待16 | 120 | |
返回超时 |
程序在重传第15次之后还等待了120秒才超时报错退出,丢弃TCP连接
|
Step5
查看两端机器的TCP连接状态,主机A上的TCP连接经过15次重传失败超时后被丢弃了,而主机B上的连接依然存在,看来主机A在丢弃连接的时候也并没有正常发送FIN包到对端
多次测试积累了一堆半打开的TCP连接

查看linux内核源码
#define TCP_RTO_MAX ((unsigned)(120*HZ))
#define TCP_RTO_MIN ((unsigned)(HZ/5))
------------------------------------------
static inline void tcp_bound_rto(const struct sock *sk)
{
if (inet_csk(sk)->icsk_rto > TCP_RTO_MAX)
inet_csk(sk)->icsk_rto = TCP_RTO_MAX;
} |
从源码中可以看到TCP_RTO是有最小值与最大值的,当实际计算出来的rto大于最大值的时候会以最大值为准,而HZ可以理解为1s,所以120*HZ就是120秒,HZ/5就是200ms
HZ表示CPU一秒种发出多少次时间中断--IRQ-0,Linux中通常用HZ来做时间片的计算
|