知乎千赞的 TCP 文章,我写错了一个点。。。
实验一:模拟 TCP 第一次握手的 SYN 丢包;
实验二:模拟 TCP 第二次握手的 SYN、ACK 丢包;
实验三:模拟 TCP 第三次握手的 ACK 包丢;
TCP 三次握手异常情况实战分析
TCP 第一次握手的 SYN 丢包了,会发生了什么?
TCP 第二次握手的 SYN、ACK 丢包了,会发生什么?
TCP 第三次握手的 ACK 包丢了,会发生什么?
那会重传几次?
超时重传的时间 RTO 会如何变化?
在 Linux 下如何设置重传次数?
….
实验场景
客户端和服务端都是 CentOs 6.5 Linux,Linux 内核版本 2.6.32
服务端 192.168.12.36,apache web 服务
客户端 192.168.12.37
实验一:TCP 第一次握手 SYN 丢包
date
返回的时间,可以发现在超时接近 1 分钟的时间后,curl 返回了错误。第一次是在 1 秒超时重传
第二次是在 3 秒超时重传
第三次是在 7 秒超时重传
第四次是在 15 秒超时重传
第五次是在 31 秒超时重传
SYN
超时重传次数,是如下内核参数指定的:$ cat /proc/sys/net/ipv4/tcp_syn_retries
5
tcp_syn_retries
默认值为 5,也就是 SYN 最大重传次数是 5 次。tcp_syn_retries
设置为 2 次:$ echo 2 > /proc/sys/net/ipv4/tcp_syn_retries
实验一的实验小结
tcp_syn_retries
值后,客户端不再发送 SYN 包。实验二:TCP 第二次握手 SYN、ACK 丢包
date
返回的时间前后,可以算出大概 1 分钟后,curl 报错退出了。客户端发起 SYN 后,由于防火墙屏蔽了服务端的所有数据包,所以 curl 是无法收到服务端的 SYN、ACK 包,当发生超时后,就会重传 SYN 包
服务端收到客户的 SYN 包后,就会回 SYN、ACK 包,但是客户端一直没有回 ACK,服务端在超时后,重传了 SYN、ACK 包,接着一会,客户端超时重传的 SYN 包又抵达了服务端,服务端收到后,然后回了 SYN、ACK 包,但是SYN、ACK包的重传定时器并没有重置,还持续在重传,因为第二次握手在没收到第三次握手的 ACK 确认报文时,就会重传到最大次数。
最后,客户端 SYN 超时重传次数达到了 5 次(tcp_syn_retries 默认值 5 次),就不再继续发送 SYN 包了。
咦?客户端设置了防火墙,屏蔽了服务端的网络包,为什么 tcpdump 还能抓到服务端的网络包?
如果添加的是
INPUT
规则,则可以抓得到包如果添加的是
OUTPUT
规则,则抓不到包
进来的顺序 Wire -> NIC -> tcpdump -> netfilter/iptables
出去的顺序 iptables -> tcpdump -> NIC -> Wire
tcp_syn_retries 是限制 SYN 重传次数,那第二次握手 SYN、ACK 限制最大重传次数是多少?
tcp_synack_retries
内核参数限制的,其默认值如下:$ cat /proc/sys/net/ipv4/tcp_synack_retries
5
5
次。tcp_syn_retries
设置为 1,表示客户端 SYN 最大超时次数是 1 次,目的是为了防止多次重传 SYN,把服务端 SYN、ACK 超时定时器重置。客户端配置防火墙屏蔽服务端的数据包
客户端 tcpdump 抓取 curl 执行时的数据包
客户端的 SYN 只超时重传了 1 次,因为
tcp_syn_retries
值为 1服务端应答了客户端超时重传的 SYN 包后,由于一直收不到客户端的 ACK 包,所以服务端一直在超时重传 SYN、ACK 包,每次的 RTO 也是指数上涨的,一共超时重传了 5 次,因为
tcp_synack_retries
值为 5
tcp_syn_retries
依然设置为 1:$ echo 2 > /proc/sys/net/ipv4/tcp_synack_retries
$ echo 1 > /proc/sys/net/ipv4/tcp_syn_retries
客户端的 SYN 包只超时重传了 1 次,符合 tcp_syn_retries 设置的值;
服务端的 SYN、ACK 超时重传了 2 次,符合 tcp_synack_retries 设置的值
实验二的实验小结
实验三:TCP 第三次握手 ACK 丢包
SYN_RECV
状态:ESTABLISHED
状态:ESTABLISHED
状态:为什么服务端原本处于
SYN_RECV
状态的连接,过 1 分钟后就消失了?为什么客户端 telnet 输入 123456 字符后,过了好长一段时间,telnet 才断开连接?
客户端发送 SYN 包给服务端,服务端收到后,回了个 SYN、ACK 包给客户端,此时服务端的 TCP 连接处于
SYN_RECV
状态;客户端收到服务端的 SYN、ACK 包后,给服务端回了个 ACK 包,此时客户端的 TCP 连接处于
ESTABLISHED
状态;由于服务端配置了防火墙,屏蔽了客户端的 ACK 包,所以服务端一直处于
SYN_RECV
状态,没有进入ESTABLISHED
状态,tcpdump 之所以能抓到客户端的 ACK 包,是因为数据包进入系统的顺序是先进入 tcpudmp,后经过 iptables;接着,服务端超时重传了 SYN、ACK 包,重传了 5 次后,也就是超过 tcp_synack_retries 的值(默认值是 5),然后就没有继续重传了,此时服务端的 TCP 连接主动中止了,所以刚才处于 SYN_RECV 状态的 TCP 连接断开了,而客户端依然处于
ESTABLISHED
状态;虽然服务端 TCP 断开了,但过了一段时间,发现客户端依然处于
ESTABLISHED
状态,于是就在客户端的 telnet 会话输入了 123456 字符;此时由于服务端已经断开连接,客户端发送的数据报文,一直在超时重传,每一次重传,RTO 的值是指数增长的,所以持续了好长一段时间,客户端的 telnet 才报错退出了,此时共重传了 15 次。
服务端在重传 SYN、ACK 包时,超过了最大重传次数
tcp_synack_retries
,于是服务端的 TCP 连接主动断开了。客户端向服务端发送数据包时,由于服务端的 TCP 连接已经退出了,所以数据包一直在超时重传,共重传了 15 次, telnet 就断开了连接。
TCP 第一次握手的 SYN 包超时重传最大次数是由 tcp_syn_retries 指定,TCP 第二次握手的 SYN、ACK 包超时重传最大次数是由 tcp_synack_retries 指定,那 TCP 建立连接后的数据包最大超时重传次数是由什么参数指定呢?
tcp_retries2
指定,默认值是 15 次,如下:$ cat /proc/sys/net/ipv4/tcp_retries2
15
那如果客户端不发送数据,什么时候才会断开处于 ESTABLISHED 状态的连接?
net.ipv4.tcp_keepalive_time=7200
net.ipv4.tcp_keepalive_intvl=75
net.ipv4.tcp_keepalive_probes=9
tcp_keepalive_time=7200:表示保活时间是 7200 秒(2小时),也就 2 小时内如果没有任何连接相关的活动,则会启动保活机制
tcp_keepalive_intvl=75:表示每次检测间隔 75 秒;
tcp_keepalive_probes=9:表示检测 9 次无响应,认为对方是不可达的,从而中断本次的连接。
实验三的实验小结
SYN_RECV
状态,而客户端会处于 ESTABLISHED
状态。tcp_synack_retries
值(默认值 5 次)后,服务端就会断开 TCP 连接。如果客户端没发送数据包,一直处于
ESTABLISHED
状态,然后经过 2 小时 11 分 15 秒才可以发现一个「死亡」连接,于是客户端连接就会断开连接。如果客户端发送了数据包,一直没有收到服务端对该数据包的确认报文,则会一直重传该数据包,直到重传次数超过
tcp_retries2
值(默认值 15 次)后,客户端就会断开 TCP 连接。