前些天,有朋友问我关于 FIN_WAIT2 的问题:如果主动关闭的一方在进入 FIN_WAIT2 状态后没有收到被动关闭的一方发送的 FIN 包,那么会怎样?
让我们热热身,通过一张旧图来回忆一下 TCP 关闭连接时的情况:
按照正常的状态迁移路径,当 FIN_WAIT2 收到 FIN 包后会迁移到 TIME_WAIT 状态。如果没有收到 FIN 包,那么连接状态会如何迁移,我们不妨测试一下:
#!/usr/bin/env python import socket import time s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('127.0.0.1', 1234)) s.listen(1) c, _ = s.accept() time.sleep(1000) c.close()
如上是用 Python 实现的一个简单的 server 演示代码,需要注意的是我在 close 前设置了一个巨大的延迟时间,从而达到拖延服务端发出 FIN 包的目的。与之相对应的我们再实现一个简单的 client 演示代码,它没什么可说的,就是连上后直接关闭:
#!/usr/bin/env python import socket import time s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('127.0.0.1', 1234)) s.close()
测试的时候先启动服务端监听 1234 端口,再启动客户端连接 1234 端口,为了确认整个过程是否符合我们的预期,可以使用 tcpdump 监听通讯过程:
如图可见:在三次握手之后,客户端关闭了连接,服务端确认后没有发出 FIN 包,所以整个过程符合我们的预期,同时为了判断 FIN_WAIT2 存在了多久,写了如下代码:
shell> while sleep 1; do netstat -ant | grep FIN_WAIT2 | while read content; do echo -n $(date +"%T") "" echo $content done done
监控发现,在本例中 FIN_WAIT2 存在的时间大约是一分钟左右:
实际上此时间是「net.ipv4.tcp_fin_timeout」控制的,不过在测试中发现,FIN_WAIT2 存在的时间并不是精确的等于 tcp_fin_timeout 的设置,存在一定的偏差。此外,需要说明的是在 tcp_fin_timeout 后,FIN_WAIT2 并没有迁移到 TIME_WAIT,而是直接关闭了。
关于 tcp_fin_timeout 的介绍,可以参考内核的说明:
The length of time an orphaned (no longer referenced by any application) connection will remain in the FIN_WAIT_2 state before it is aborted at the local end. While a perfectly valid “receive only” state for an un-orphaned connection, an orphaned connection in FIN_WAIT_2 state could otherwise wait forever for the remote to close its end of the connection.
Cf. tcp_max_orphans
Default: 60 seconds
介绍中提到了 orphan 的概念,指的是当一个 socket 不再被任何应用引用时,它便成为了一个孤儿,而 tcp_fin_timeout 限定了成为孤儿的 FIN_WAIT2 所能存活的时间。
注:orphan 太多的话会:The “Out of socket memory” error。
或许有人会问:如果 FIN_WAIT2 没有成为孤儿,那么会如何:
#!/usr/bin/env python import socket import time s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('127.0.0.1', 1234)) s.shutdown(socket.SHUT_WR) time.sleep(1000) s.close()
在这一版的 client 代码中,我们没有直接 close 连接,而是通过 shutdown 来关闭,此时客户端同样会发送 FIN 包,但是并没有释放连接,所以本例中的 FIN_WAIT2 和上例中的 FIN_WAIT2 不同,其并不会成为孤儿。通过测试发现,此时 tcp_fin_timeout 将失效,而 本例中的 FIN_WAIT2 会一直存在,直到客户端 close 连接或者退出。
实际上,同其它 TCP 状态相比,通常 FIN_WAIT2 并不会给你添什么麻烦。如果你统计服务器上的 TCP 状态,你会发现它们多数时候都很少,如果不是,那么一定是应用层面上出了问题。至于 tcp_fin_timeout,我并不建议大家把它设置得太小,因为如上所说,正常情况下,TCP 连接并不会在 FIN_WAIT2 状态上停留太久,假设真的出现 FIN 包丢失之类的情况,那么给 FIN_WAIT2 状态一个合理的存在周期是必要的,毕竟丢失的 FIN 包可能会重传,在这一点上和 TIME_WAIT 为什么要存在 2MSL 是同样的原因。既然 TIME_WAIT 缺省存在 60 秒,那么 tcp_fin_timeout 缺省设置为 60 同样是一个合理的选择。
评论前必须登录!
注册