TCP--telnet为何在127s后返回?

背景

近期编写了监控业务服务器的脚本,主要原理是用shell脚本(运行shell的机器称之为监控机)调用项目组专用的接口测试工具,对指定的业务服务器进行业务操作,根据接口测试工具的返回结果判断业务服务器是否运行正常,并使用crontab设置每分钟执行一次监控脚本。

在接口测试工具启动前,先使用telnet ip port判断了业务服务器的端口是否打开。监控工作如期进行着……

然而,当新增对某个业务服务器进行监控时,发现telnet该业务服务器所花费的时间很长,已经超过了1分钟,这意味着脚本不能在预定的1分钟的监控间隔内执行完毕,于是我针对该问题进行了探索和研究。

探索和研究

从一些网页中得知可在一些特定文件中设置TMOUT变量来控制telnet的超时时间,也有网页中提到可以使用nc命令代替telnet实现同类效果并可以设置超时时间——还没来得及作深入了解,测试出telnet该业务服务器的所需时长:

time telnet 123.59.208.201 62715
Trying 123.59.208.201...
telnet: connect to address 123.59.208.201: Connection timed out
telnet 123.59.208.201 62715  0.00s user 0.00s system 0% cpu 2:07.29 total

结果显示时长为2:07.29,即127s

常见的超时时间,应多是分钟的倍数,如60s、3600s等,又或者应是10的倍数,如30s、200s等。但反复测试了几次,均是127s。它的特殊性引起了我的好奇,因为127并非是一个普通的数字,它正是2的7次方-1。于是,我修正了搜索的方向。终于,从搜索返回的结果页中看到了一个链接,原文链接

http://www.chengweiyang.cn/2017/02/18/linux-connect-timeout/?utm_source=tuicool&utm_medium=referral

这篇文章解决了我此时的疑惑,也开启了新的探索之旅。

127的由来

使用tcpdump进行抓包,运行如下命令:

sudo tcpdump -i eth0 -nn 'host 123.59.208.201'
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes

再新打开一个终端窗口,执行telnet命令:

date +%Y%m%d-%H:%M:%S.%N;time telnet 123.59.208.201 62715;date +%Y%m%d-%H:%M:%S.%N;
20170526-18:04:23.764397558
Trying 123.59.208.201...
telnet: connect to address 123.59.208.201: Connection timed out
telnet 123.59.208.201 62715  0.00s user 0.00s system 0% cpu 2:07.22 total
20170526-18:06:30.989480391

这时,tcpdump所在的终端会同步输出telnet命令所产生的数据包信息:

18:04:23.765507 IP 10.253.4.55.34680 > 123.59.208.201.62715: Flags [S], seq 922947731, win 29200, options [mss 1460,sackOK,TS val 13801993 ecr 0,nop,wscale 7], length 0
18:04:24.768182 IP 10.253.4.55.34680 > 123.59.208.201.62715: Flags [S], seq 922947731, win 29200, options [mss 1460,sackOK,TS val 13802996 ecr 0,nop,wscale 7], length 0
18:04:26.772188 IP 10.253.4.55.34680 > 123.59.208.201.62715: Flags [S], seq 922947731, win 29200, options [mss 1460,sackOK,TS val 13805000 ecr 0,nop,wscale 7], length 0
18:04:30.780189 IP 10.253.4.55.34680 > 123.59.208.201.62715: Flags [S], seq 922947731, win 29200, options [mss 1460,sackOK,TS val 13809008 ecr 0,nop,wscale 7], length 0
18:04:38.796205 IP 10.253.4.55.34680 > 123.59.208.201.62715: Flags [S], seq 922947731, win 29200, options [mss 1460,sackOK,TS val 13817024 ecr 0,nop,wscale 7], length 0
18:04:54.828196 IP 10.253.4.55.34680 > 123.59.208.201.62715: Flags [S], seq 922947731, win 29200, options [mss 1460,sackOK,TS val 13833056 ecr 0,nop,wscale 7], length 0
18:05:26.860210 IP 10.253.4.55.34680 > 123.59.208.201.62715: Flags [S], seq 922947731, win 29200, options [mss 1460,sackOK,TS val 13865088 ecr 0,nop,wscale 7], length 0

根据telnet命令的起始时间和tcpdump输出的时间:

04:23.76
04:24.76
04:26.77
04:30.78
04:38.79
04:54.82
05:26.86
06:30.98

不难看出规律:它们的差值(单位s)是双倍递增的:1、2、4、8、16、32、64

内部原理

tcp超时与重传

执行telnet命令,其实是尝试建立tcp连接的过程。而建立tcp连接时会涉及到两个概念:一个是RTT,一个是RTO。
RTT (Round-Trip Time) 即,往返时间
RTO (Retransmission Time Out) 即,重传超时
上面的时间间隔显然就是RTO。

建立tcp连接的过程和细节这里不再赘述,如有需要可以参阅《TCPIP详解(卷1)》。

根据该监控机的内核的版本,查看了linux的相关代码:
http://elixir.free-electrons.com/linux/v3.10/source/include/net/tcp.h

#define TCP_RTO_MAX ((unsigned)(120*HZ))
#define TCP_RTO_MIN ((unsigned)(HZ/5))

得到:

  1. HZ是1s。所以RTO的最小值是200ms,RTO的最大值是120s,即2分钟。
  2. tcpdump的结果看,监控机的RTO是1s,原因未知
  3. 根据代码中的算法部分可知,RTO重传间隔是指数增加的,随着重传次数的增多,消耗的时间指数增长。当RTO小于TCP_RTO_MAX时,则RTO每次翻倍,当超过TCP_RTO_MAX之后,不再翻倍,而是固定用TCP_RTO_MAX,即2分钟。

而,linux内核变量net.ipv4.tcp_syn_retries用来告诉内核,当尝试新建一个TCP连接时,要重新发送多少次初始的SYN报文。可以通过 sysctl 命令查看。

查看变量tcp_syn_retries的值

sudo sysctl net.ipv4.tcp_syn_retries
net.ipv4.tcp_syn_retries = 6

因为tcp_syn_retries控制的是重新发送的次数,所以加上初始的那1次,所以上述tcpdump的输出中一共有7条。

设置变量tcp_syn_retries的值

设置net.ipv4.tcp_syn_retries并测试telnet的时长:

sudo sysctl net.ipv4.tcp_syn_retries=1

net.ipv4.tcp_syn_retries = 1

验证:

time telnet 123.59.208.201 62715      
Trying 123.59.208.201...
telnet: connect to address 123.59.208.201: Connection timed out
telnet 123.59.208.201 62715  0.00s user 0.00s system 0% cpu 3.008 total

可见,net.ipv4.tcp_syn_retries = 1时,telnet的耗时约为3s。可以预知的是,如果用tcpdump抓包,应当有两条,而3s则是RTO分别取1s和2s的结果。

sudo sysctl net.ipv4.tcp_syn_retries=2
net.ipv4.tcp_syn_retries = 2
time telnet 123.59.208.201 62715 
Trying 123.59.208.201...
telnet: connect to address 123.59.208.201: Connection timed out
telnet 123.59.208.201 62715  0.00s user 0.00s system 0% cpu 7.010 total

net.ipv4.tcp_syn_retries = 2时,telnet的耗时约为7s,遵守RTO指数递增规律。

sudo sysctl net.ipv4.tcp_syn_retries=5
net.ipv4.tcp_syn_retries = 5
time telnet 123.59.208.201 62715      
Trying 123.59.208.201...
telnet: connect to address 123.59.208.201: Connection timed out
telnet 123.59.208.201 62715  0.00s user 0.00s system 0% cpu 1:03.14 total

net.ipv4.tcp_syn_retries = 5时,telnet的耗时约为63s,遵守RTO指数递增规律。

那么,按照RTO指数递增规律和TCP_RTO_MAX的设定,当net.ipv4.tcp_syn_retries持续增大时,RTO并不会对应地指数递增下去,把net.ipv4.tcp_syn_retries设置成10看看效果:

sudo sysctl net.ipv4.tcp_syn_retries=10
net.ipv4.tcp_syn_retries = 10

验证:

date +%Y%m%d-%H:%M:%S.%N;time telnet 123.59.208.201 62715;date +%Y%m%d-%H:%M:%S.%N;

20170526-18:47:25.899074041
Trying 123.59.208.201...
telnet: connect to address 123.59.208.201: Connection timed out
telnet 123.59.208.201 62715  0.00s user 0.00s system 0% cpu 10:08.64 total
20170526-18:57:34.541575652

查看tcpdump的结果:

18:47:25.900294 IP 10.253.4.55.36266 > 123.59.208.201.62715: Flags [S], seq 3545463057, win 29200, options [mss 1460,sackOK,TS val 16384128 ecr 0,nop,wscale 7], length 0
18:47:26.902184 IP 10.253.4.55.36266 > 123.59.208.201.62715: Flags [S], seq 3545463057, win 29200, options [mss 1460,sackOK,TS val 16385130 ecr 0,nop,wscale 7], length 0
18:47:28.908195 IP 10.253.4.55.36266 > 123.59.208.201.62715: Flags [S], seq 3545463057, win 29200, options [mss 1460,sackOK,TS val 16387136 ecr 0,nop,wscale 7], length 0
18:47:32.916178 IP 10.253.4.55.36266 > 123.59.208.201.62715: Flags [S], seq 3545463057, win 29200, options [mss 1460,sackOK,TS val 16391144 ecr 0,nop,wscale 7], length 0
18:47:40.940213 IP 10.253.4.55.36266 > 123.59.208.201.62715: Flags [S], seq 3545463057, win 29200, options [mss 1460,sackOK,TS val 16399168 ecr 0,nop,wscale 7], length 0
18:47:56.972221 IP 10.253.4.55.36266 > 123.59.208.201.62715: Flags [S], seq 3545463057, win 29200, options [mss 1460,sackOK,TS val 16415200 ecr 0,nop,wscale 7], length 0
18:48:29.004213 IP 10.253.4.55.36266 > 123.59.208.201.62715: Flags [S], seq 3545463057, win 29200, options [mss 1460,sackOK,TS val 16447232 ecr 0,nop,wscale 7], length 0
18:49:33.132192 IP 10.253.4.55.36266 > 123.59.208.201.62715: Flags [S], seq 3545463057, win 29200, options [mss 1460,sackOK,TS val 16511360 ecr 0,nop,wscale 7], length 0
18:51:33.580213 IP 10.253.4.55.36266 > 123.59.208.201.62715: Flags [S], seq 3545463057, win 29200, options [mss 1460,sackOK,TS val 16631808 ecr 0,nop,wscale 7], length 0
18:53:33.900217 IP 10.253.4.55.36266 > 123.59.208.201.62715: Flags [S], seq 3545463057, win 29200, options [mss 1460,sackOK,TS val 16752128 ecr 0,nop,wscale 7], length 0
18:55:34.220216 IP 10.253.4.55.36266 > 123.59.208.201.62715: Flags [S], seq 3545463057, win 29200, options [mss 1460,sackOK,TS val 16872448 ecr 0,nop,wscale 7], length 0

当RTO18:48:29.00 -> 18:49:33.13到达64s后,下一次按照递增规则应当变为128s,而实际上,18:49:33.13 -> 18:51:33.58 -> 18:53:33.90 -> 18:55:34.22,RTO变为恒定的120s(TCP_RTO_MAX)。

取值范围

设置成0次会报错:

sudo sysctl net.ipv4.tcp_syn_retries=0
sysctl: setting key "net.ipv4.tcp_syn_retries": Invalid argument
net.ipv4.tcp_syn_retries = 0

不少网页上提到net.ipv4.tcp_syn_retries的最大值可设置为255、默认值是5,在这台监控机上实测发现最大只能设置为127:

sudo sysctl net.ipv4.tcp_syn_retries=127
net.ipv4.tcp_syn_retries = 127

尝试设置成128时保错:

sudo sysctl net.ipv4.tcp_syn_retries=128
sysctl: setting key "net.ipv4.tcp_syn_retries": Invalid argument
net.ipv4.tcp_syn_retries = 128

在linux内核的相关网页上有明确记录——最大不超过127、默认值为6
https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt

tcp_syn_retries - INTEGER
Number of times initial SYNs for an active TCP connection attempt
will be retransmitted. Should not be higher than 127. Default value
is 6, which corresponds to 63seconds till the last retransmission
with the current initial RTO of 1second. With this the final timeout
for an active TCP connection attempt will happen after 127seconds.

但,指出net.ipv4.tcp_syn_retries的最大值可设置为255的网页中,有不少网页看起来也具有一定的可信度,比如:

https://www.frozentux.net/ipsysctl-tutorial/chunkyhtml/tcpvariables.html

3.3.24. tcp_syn_retries
The tcp_syn_retries variable tells the kernel how many times to try to retransmit the initial SYN packet for an active TCP connection attempt.
This variable takes an integer value, but should not be set higher than 255 since each retransmission will consume huge amounts of time as well as some amounts of bandwidth. Each connection retransmission takes aproximately 30-40 seconds. The default setting is 5, which would lead to an aproximate of 180 seconds delay before the connection times out.

随着根据实测结果和网站的权威性,我更信赖最大不超过127、默认值为6,但同时也很好奇,这些网页的内容依据出自何处?

永久修改

上述提到的修改net.ipv4.tcp_syn_retries的方式均是临时修改,重启系统后便失效。若想永久生效,则需要修改相应的配置文件,请自行搜索。

另外,因为变量net.ipv4.tcp_syn_retries是系统范围的,而它影响tcp建立连接的重传次数,所以修改前建议与系统管理员确认。

总结

美其名曰探索和研究,实际就是在踩自己因为基础知识欠缺而埋下的坑

虽然搞清楚了127的由来,但却带来了许多新的疑问和不解,作为下一次踩坑的引子。

参考链接

踩坑的过程,总能发现一些不错的资源网站,如:

  1. https://www.kernel.org
  2. http://lxr.linux.no/
  3. http://free-electrons.com/docs/

我想,这算得上对困而学之者的最好奖励了。大概也是为了让我能够成为学而知之者吧。

相关知识

  • tcp协议
  • tcpdump命令
  • sysctl命令
  • telnet命令
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

  • 简介 用简单的话来定义tcpdump,就是:dump the traffic on a network,根据使用者...
    保川阅读 5,947评论 1 13
  • 18.1 引言 TCP是一个面向连接的协议。无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接。本章将...
    张芳涛阅读 3,351评论 0 13
  • 1、TCP状态linux查看tcp的状态命令:1)、netstat -nat 查看TCP各个状态的数量2)、lso...
    北辰青阅读 9,414评论 0 11
  • 1.这篇文章不是本人原创的,只是个人为了对这部分知识做一个整理和系统的输出而编辑成的,在此郑重地向本文所引用文章的...
    SOMCENT阅读 13,051评论 6 174
  • 个人认为,Goodboy1881先生的TCP /IP 协议详解学习博客系列博客是一部非常精彩的学习笔记,这虽然只是...
    贰零壹柒_fc10阅读 5,051评论 0 8