Content #
- 在服务端,执行下面的这条命令,让 iptables 静默丢弃掉发往自己 80 端口的数据包:
iptables -I INPUT -p tcp --dport 80 -j DROP
- 在客户端启动 tcpdump 抓包:
sudo tcpdump -i any -w telnet-80.pcap port 80
- 从客户端发起一次 telnet:
telnet 服务端IP 80
这个 telnet 会挂起,大约一两分钟后才会失败退出。原因在于:握手请求一直没成功。客户端一共有 7 个 SYN 包发出,或者说,除了第一次 SYN,后续还有 6 次重试。数据包之间的时间间隔,会是 1 秒,2 秒,4.2 秒,8.2 秒,16.1 秒,33 秒,每个间隔是上一个的两倍左右。到第 6 次重试失败后,客户端就彻底放弃了。
这里的翻倍时间,就是“指数退避”(Exponential backoff)原则的体现。这里的时间不是精确的整秒,因为指数退避原则本身就不建议在精确的整秒做重试,最好是有所浮动,这样可以让重试成功的机会变得更大一些。
TCP 握手没响应的话,操作系统会做重试。在 Linux 中,这个设置是由内核参数 net.ipv4.tcp_syn_retries 控制的,默认值为 6,也就是我们前面刚观察到的现象。以下就是 Ubuntu 20.04 测试机的配置:
$ sudo sysctl net.ipv4.tcp_syn_retries
net.ipv4.tcp_syn_retries = 6
还有另外好几个有关 TCP 重试的设置值,也都可以调整。更全面的内容呢,你可以直接 man tcp,查看 tcp 的内核手册的信息。比如下面就是对于 tcp_syn_retries 的解释:
tcp_syn_retries (integer; default: 5; since Linux 2.2)
The maximum number of times initial SYNs for an active TCP connection attempt will be retransmitted. This value should not be higher than 255. The default value is 5, which corresponds to approximately 180 seconds.