在RTO的计算方法中,介绍了RFC6298对于RTO的计算和RTO timer的管理算法。

但有一个重要的问题RFC没有提到,那就是如果出现了超时重传,那重传多少次可以放弃呢?

当然这是一个实现相关的细节,不同的操作系统可能有不同的实现策略。

在这篇wiki中,就来介绍一下Linux中是怎么限制超时重传次数的。

听说Linux有两个参数限制超时重传次数

没错,Linux中确实定义了两个参数来限定超时重传的次数的,以下是源码中Documentation/networking/ip-sysctl.txt文档中的描述

tcp_retries1 - INTEGER

This value influences the time, after which TCP decides, that

something is wrong due to unacknowledged RTO retransmissions,

and reports this suspicion to the network layer.

See tcp_retries2 for more details.

RFC 1122 recommends at least 3 retransmissions, which is the

default.

tcp_retries2 - INTEGER

This value influences the timeout of an alive TCP connection,

when RTO retransmissions remain unacknowledged.

Given a value of N, a hypothetical TCP connection following

exponential backoff with an initial RTO of TCP_RTO_MIN would

retransmit N times before killing the connection at the (N+1)th RTO.

The default value of 15 yields a hypothetical timeout of 924.6

seconds and is a lower bound for the effective timeout.

TCP will effectively time out at the first RTO which exceeds the

hypothetical timeout.

RFC 1122 recommends at least 100 seconds for the timeout,

which corresponds to a value of at least 8.

就是这样一段话,可能由于过于概括,会令人产生很多疑问,甚至产生一些误解。

比如常见的问题有:

a. 超过tcp_retries1这个阈值后,到底是report了怎样一种suspicion呢?

b. tcp_retries1和tcp_retries2的数字是表示RTO重传的次数上限,对吗?

c. 文档中提到,924.6s is a lower bound for the effective timeout。

这里的effective timeout是指什么?

为什么是lower bound,tcp_retries2不应该是限制重传次数的upper bound吗?

下面就结合Linux 3.10的源码来逐个解释一下以上几个问题。并在最后给出一个总结。

重传超过tcp_retries1会怎样

文档中说的suspicion到底是什么呢?来看一下tcp_retries1相关的代码部分

// RTO timer的处理函数是tcp_retransmit_timer(),与tcp_retries1相关的代码调用关系如下

tcp_retransmit_timer()

=> tcp_write_timeout() // 判断是否重传了足够的久

=> retransmit_timed_out(sk, sysctl_tcp_retries1, 0, 0) // 判断是否超过了阈值

// tcp_write_timeout()的具体相关内容

...

if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {

// 如果超时发生在三次握手期间,此时有专门的tcp_syn_retries来负责限定重传次数

...

} else { // 如果超时发生在数据发送期间

// 这个函数负责判断重传是否超过阈值,返回真表示超过。后续会详细分析这个函数

if (retransmits_timed_out(sk, sysctl_tcp_retries1, 0, 0)) {

/* Black hole detection */

tcp_mtu_probing(icsk, sk); // 如果开启tcp_mtu_probing(默认关闭)了,则执行PMTU

dst_negative_advice(sk); // 更新路由缓存

}

...

}

从以上的代码可以看到,一旦重传超过阈值tcp_retries1,主要的动作就是更新路由缓存。

用以避免由于路由选路变化带来的问题。

重传超过tcp_retries2会怎样

会直接放弃重传,关闭TCP流

// 依然还是在tcp_write_timeout()中,retry_until一般是tcp_retries2

...

if (retransmits_timed_out(sk, retry_until, syn_set ? 0 : icsk->icsk_user_timeout, syn_set)) {

/* Has it gone just too far? */

tcp_write_err(sk); // 调用tcp_done关闭TCP流

return 1;

}

retries限制的重传次数吗

咋一看文档,很容易想到retries的数字就是限定的重传的次数,甚至源码中对于retries常量注释中都写着”This is how many retries it does…”

#define TCP_RETR1 3 /*

* This is how many retries it does before it

* tries to figure out if the gateway is

* down. Minimal RFC value is 3; it corresponds

* to ~3sec-8min depending on RTO.

*/

#define TCP_RETR2 15 /*

* This should take at least

* 90 minutes to time out.

* RFC1122 says that the limit is 100 sec.

* 15 is ~13-30min depending on RTO.

*/

那就就来看看retransmits_timed_out的具体实现,看看到底是不是限制的重传次数

/* This function calculates a "timeout" which is equivalent to the timeout of a

* TCP connection after "boundary" unsuccessful, exponentially backed-off

* retransmissions with an initial RTO of TCP_RTO_MIN or TCP_TIMEOUT_INIT if

* syn_set flag is set.

*/

static bool retransmits_timed_out(struct sock *sk,

unsigned int boundary,

unsigned int timeout,

bool syn_set)

{

unsigned int linear_backoff_thresh, start_ts;

// 如果是在三次握手阶段,syn_set为真

unsigned int rto_base = syn_set ? TCP_TIMEOUT_INIT : TCP_RTO_MIN;

if (!inet_csk(sk)->icsk_retransmits)

return false;

// retrans_stamp记录的是数据包第一次发送的时间,在tcp_retransmit_skb()中设置

if (unlikely(!tcp_sk(sk)->retrans_stamp))

start_ts = TCP_SKB_CB(tcp_write_queue_head(sk))->when;

else

start_ts = tcp_sk(sk)->retrans_stamp;

// 如果用户态未指定timeout,则算一个出来

if (likely(timeout == 0)) {

/* 下面的计算过程,其实就是算一下如果以rto_base为第一次重传间隔,

* 重传boundary次需要多长时间

*/

linear_backoff_thresh = ilog2(TCP_RTO_MAX/rto_base);

if (boundary <= linear_backoff_thresh)

timeout = ((2 << boundary) - 1) * rto_base;

else

timeout = ((2 << linear_backoff_thresh) - 1) * rto_base +

(boundary - linear_backoff_thresh) * TCP_RTO_MAX;

}

// 如果数据包第一次发送的时间距离现在的时间间隔,超过了timeout值,则认为重传超于阈值了

return (tcp_time_stamp - start_ts) >= timeout;

}

从以上的代码分析可以看到,真正起到限制重传次数的并不是真正的重传次数。

而是以tcp_retries1或tcp_retries2为boundary,以rto_base(如TCP_RTO_MIN 200ms)为初始RTO,计算得到一个timeout值出来。如果重传间隔超过这个timeout,则认为超过了阈值。

上面这段话太绕了,下面举两个个例子来说明

以判断是否放弃TCP流为例,如果tcp_retries2=15,那么计算得到的timeout=924600ms。

1. 如果RTT比较小,那么RTO初始值就约等于下限200ms

由于timeout总时长是924600ms,表现出来的现象刚好就是重传了15次,超过了timeout值,从而放弃TCP流

2. 如果RTT较大,比如RTO初始值计算得到的是1000ms

那么根本不需要重传15次,重传总间隔就会超过924600ms。

比如我测试的一个RTT=400ms的情况,当tcp_retries2=10时,仅重传了3次就放弃了TCP流

另外几个小问题

理解了Linux决定重传次数的真实机制,就不难回答一下几个问题了

>> effective timeout指的是什么?

<< 就是retransmits_timed_out计算得到的timeout值

>> 924.6s是怎么算出来的?

<< 924.6s = (( 2 << 9) -1) * 200ms + (15 - 9) * 120s

>> 为什么924.6s是lower bound?

<< 重传总间隔必须大于timeout值,即 (tcp_time_stamp - start_ts) >= timeout

>> 那RTO超时的间隔到底是不是源码注释的"15 is ~13-30min depending on RTO."呢?

<< 显然不是! 虽然924.6s(15min)是一个lower bound,但是它同时也是一个upper bound!

怎么理解?举例说明

1. 如果某个RTO值导致,在已经重传了14次后,总重传间隔开销是924s

那么它还需要重传第15次,即使离924.6s只差0.6s。这就是发挥了lower bound的作用

2. 如果某个RTO值导致,在重传了10次后,总重传间隔开销是924s

重传第11次后,第12次超时触发时计算得到的总间隔变为1044s,超过924.6s

那么此时会放弃第12次重传,这就是924.6s发挥了upper bound的作用

总的来说,在Linux3.10中,如果tcp_retres2设置为15。总重传超时周期应该在如下范围内

[924.6s, 1044.6s)

所以综合上述,Linux并不是直接拿tcp_retries1和tcp_retries2来限制重传次数的,而是用计算得到

的一个timeout值来判断是否要放弃重传的。真正的重传次数同时与RTT相关。

linux修改重传次数,聊一聊重传次数相关推荐

  1. linux查询锁定时间,Linux限制远程登陆尝试密码次数及锁定时间

    CentOS中有一个pam_tally2.so的PAM模块,来限定用户的登陆失败次数,若是次数达到设置的阈值,则锁定用户. 一.编译PAM的配置文件 [root@node2 ~ ]# vim /etc ...

  2. Linux修改密码成功,却无法登录

    项目场景: Linux修改密码成功,却无法登录 问题描述 等保服务器密码过期后,修改密码,登录显示密码错误,后台vnc修改密码后,新密码无法登录 使用passwd修改密码 [root@localhos ...

  3. linux 修改密码后SSH登录不了

    linux修改账号密码后,密码正确一直登录不了,查看secure日志 tail -f /var/log/secure 发现登录时日志显示以下内容: Jan 10 09:18:56 localhost ...

  4. TCP的拥塞避免、超时重传、快速重传、快速恢复

    转自:http://blog.csdn.net/itmacar/article/details/12278769 感谢博主的辛勤成果! 为了防止网络的拥塞现象,TCP提出了一系列的拥塞控制机制.最初由 ...

  5. 关于TCP快速重传的细节-重传优先级与重传触发条件

    这篇文章写的有点过于细节,因此考虑到可读性和日后的可查阅性,我以两个问题作为引子.作为TCP相关项目的招聘,也可以作为面试题,不过,我敢肯定,大多数人都不能回答第一个问题,第二个问题可能会模棱两可. ...

  6. linux rpm目录,Linux修改RPM的安装目录的方法

    Linux系统下大多数人都是使用rpm -i xxxx.rpm命令来安装RPM包,这种情况下RPM会被安装在默认的目录当中.如果不想把RPM安装在这个目录下应该怎么办呢?下面就来介绍一下Linux如何 ...

  7. 如何在linux系统下修改mysql密码_如何在linux下修改mysql数据库密码?linux修改数据库密码的方法...

    本篇文章给大家带来的内容是介绍如何在linux下修改mysql数据库密码?linux修改数据库密码的方法.有一定的参考价值,有需要的朋友可以参考一下,希望对你们有所帮助. Linux下修改Mysql的 ...

  8. Linux 修改SSH 默认端口 22,防止被破解密码

    2019独角兽企业重金招聘Python工程师标准>>> Linux/Unix 系统,很多人使用SSH + 密码来登陆服务器,默认 22端口,这样会有被暴力破解密码的危险(除非密码足够 ...

  9. 修改系统路径 linux,Linux修改sudo的PATH路径的解决方法

    众所周知,Linux系统sudo命令可以搜索PATH下的命令,但是有时候明明PATH下有某些命令,为什么sudo就是搜索不到.这可能是因为Linux自动对PATH进行重置,改变了sudo命令搜索的路径 ...

最新文章

  1. 机器学习入门(04)— 神经网络多维数组 NumPy 相乘运算
  2. eas账号是什么意思_刚开始做抖音带货和好物推荐,如何布局抖音种草账号矩阵?...
  3. UICollectionView入门--使用系统UICollectionViewFlowLayout布局类
  4. .net生成excel并弹出保存提示框(转载)
  5. 人工智能:第八章 自动规划
  6. 应用跳转到AppStore指定关键字搜索界面
  7. 反思项目调试整体过程
  8. Windows访问Linux的Tomcat,显示无法连接
  9. 显示计算机硬盘驱动器更改,计算机更换硬盘驱动器后蓝屏发生了什么
  10. MyGeneration的NHibernate代码生成模版
  11. NSURLCache缓存使用简介
  12. java 在已有的so基础上封装jni_webshell中的分离免杀实践java篇
  13. 盘点无人机的关键技术点
  14. 李沐新书中文版上线,零基础也可以《动手学深度学习》| 这不是0.7版
  15. Python调shell
  16. aliyun redis 链接超时_超详细的Redis入门指导
  17. Turbo C 2.0下载地址和安装教程(图解)
  18. 802.11无线WIFI协议学习笔记(一)
  19. python谢尔宾斯基三角形
  20. UE_GPU Driven Pipeline Mesh Shader(meshlet)

热门文章

  1. 直接学python行不行_是否可以直接学python或者java而不学c++?
  2. java序列化异常_Java|序列化异常StreamCorruptedException的解决方法
  3. 幂等校验是什么意思_阿里面试官:接口的幂等性怎么设计?
  4. 手机屏幕镜像翻转软件_可以把ipad投屏到电视吗?屏幕镜像一键投屏
  5. Qt / QWidget、QFrame 区别
  6. python / 解决 pyinstaller 打包后运行时提示找不到模块的问题
  7. 在线GUI编译分享|8ms模拟器的使用
  8. blob html 预览_iframe和HTML5 blob实现JS,CSS,HTML直接当前页预览
  9. 怎么更新鸿蒙系统mate10,能不能升级鸿蒙系统?
  10. get request uni 参数_uni-app 环境配置,uni.request封装,接口配置,全局配置,接口调用的封装...