IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    关于FIN_WAIT2

    老王发表于 2016-09-05 14:30:52
    love 0

    前些天,有朋友问我关于 FIN_WAIT2 的问题:如果主动关闭的一方在进入 FIN_WAIT2 状态后没有收到被动关闭的一方发送的 FIN 包,那么会怎样?

    让我们热热身,通过一张旧图来回忆一下 TCP 关闭连接时的情况:

    TCP Close

    TCP Close

    按照正常的状态迁移路径,当 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 监听通讯过程:

    tcpdump -t -nn -i any port 1234

    tcpdump -t -nn -i any port 1234

    如图可见:在三次握手之后,客户端关闭了连接,服务端确认后没有发出 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 存在的时间大约是一分钟左右:

    FIN_WAIT2 存在的时间

    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 同样是一个合理的选择。



沪ICP备19023445号-2号
友情链接