系统性能优化-7 TCP 四次挥手
关闭连接的调用函数分为两种:
- close close 函数会让连接变为孤儿连接,此时该连接句柄已释放,发送 FIN 报文,由内核和对方进行挥手交互,因此此时即使接收方继续发送数据,应用进程也接收不到了
# 查看孤儿 socket 数
cat /proc/net/sockstat
sockets: used 133
TCP: inuse 9 orphan 0 tw 3 alloc 9 mem 1
UDP: inuse 2 mem 0
UDPLITE: inuse 0
RAW: inuse 0
FRAG: inuse 0 memory 0
- shutdown shutdown 函数的不同传参允许在半关闭的连接上长时间传输数据
主动方发送 FIN 报文,进入 FIN-WAIT1 状态,此时如果 FIN 报文丢失,会进行重发,重发次数默认为 8 次,由 tcp_orphan_retries 控制,默认值是 0,代表 8 次,如果改为 -1,代表无限制,会尽可能重试
sysctl net.ipv4.tcp_orphan_retries
如果 FIN_WAIT1 状态连接有很多,就需要考虑降低 tcp_orphan_retries 的值
但是 TCP 具有顺序发送和拥塞控制等特性,如果发送缓冲区前面一直有数据,FIN 报文是发送不出去的,同时如果对方恶意修改接收窗口为 0,FIN 也发送不出去,连接一直处于 FIN_WAIT1 状态。解决方案是修改 tcp_max_orphans,该字段定义了系统的最大孤儿连接数,当超出这个数目时,不走四次挥手而是直接发送 RST 复位报文强制关闭
sysctl net.ipv4.tcp_max_orphans
主动方发送 FIN 后,被动方回复 ACK,此时主动方进入 FIN-WAIT2 状态,如果是孤儿连接,这个阶段最多持续 tcp_fin_timeout 时间,如果收不到被动方的 FIN 报文,连接就会直接关闭。
sysctl net.ipv4.tcp_fin_timeout
此时被动方进入 CLOSE-WAIT 状态,如果进程调用 read 会返回 0,代表对方已经关闭发送通道了,开发人员可以据此调用本机的 close 函数关闭通信。
被动方发送 FIN 报文,进入 LAST-ACK 阶段,重试次数依然由 tcp_orphan_retries 控制,主动方收到 FIN 报文,进入 TIME-WAIT 状态,回复 ACK。TIME-WAIT 的存在是具有重要意义的,如果没有该阶段而直接关闭,可能很快有新的进程使用,此时被动方可能由于 ACK 丢失重发 FIN 报文,对主动方造成影响,该状态默认为 60s,是系统 2MSL 时长(MSL固定值30s,定义了一个报文在网络中的最长生存时间,即多数情况下 TTL 减到 0 流转的时间),这个数值是硬编码在内核中的,也就是说除非重新编译内核,否则没法修改它。
tcp_max_tw_buckets 可以限制 TIME-WAIT 状态的连接数量,当超出此数量时,新关闭的连接就不再经历 TIME_WAIT 而直接关闭。因此调大这个参数可以降低连接间数据错乱的概率,如果系统中并发连接很多时,可以适当调大该值。
sysctl net.ipv4.tcp_max_tw_buckets
而 tcp_tw_reuse 使系统可以复用 TIME-WAIT 状态的连接,作为客户端的新连接
,在安全条件下使用 TIME_WAIT 状态下的端口,如果想要使其生效,还需要开启双方的 tcp_timestamps 。
# 0 关 1 开
sysctl net.ipv4.tcp_tw_reusesysctl net.ipv4.tcp_timestamps
老版本的 Linux 还提供了 tcp_tw_recycle 参数,它并不要求 TIME_WAIT 状态存在 60 秒,很容易导致数据错乱,不建议设置为 1
。
sysctl net.ipv4.tcp_tw_recycle
如果被动方迅速调用 close 函数,那么被动方的 ACK 和 FIN 有可能在一个报文中发送,这样看起来,四次挥手会变成三次挥手,这只是一种特殊情况,不用在意。
总结一下本文提到的配置项:
- net.ipv4.tcp_orphan_retries 关闭连接时发送 FIN 时的重试次数,主动方和被动方发生 FIN 都依赖这个值,默认为 0 代表 8 次,如果改为 -1,则无限制,会尽可能尝试。如果系统中出现大量 FIN-WAIT1 状态的连接,可以适当减小。
- net.ipv4.tcp_max_orphans 最大的孤儿连接数,当超出这个数量时,不走挥手协议而是直接发送 RST 复位报文强制关闭。
- net.ipv4.tcp_fin_timeout 孤儿连接在进去 FIN-WAIT2 状态后,等待对方发送 FIN 报文的最大等待时间。
- net.ipv4.tcp_max_tw_buckets 系统允许的最大 TIMEWAIT 的连接数,如果是高并发服务,可以适当调大该值,根据
net.ipv4.ip_local_port_range
可用的端口数进行调整。 - net.ipv4.tcp_tw_reuse 复用 TIMEWAIT 状态连接作为客户端的新连接,需要结合 net.ipv4.tcp_timestamps 一起使用
- net.ipv4.tcp_tw_recycle TIMEWAIT 态连接的快速回收,不推荐开启。