关于关闭 Socket 的一些坑
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于关闭 Socket 的一些坑相关的知识,希望对你有一定的参考价值。
参考技术A <div style="text-align: right">LexusLee</div>最近踩到一个 "Socket 连接持续处于 Fin_Wait2 和 Close_Wait 状态无法关闭" 的坑中。起因是在维护大量连接时调用 socket.close() 时,看到部分连接并没有正常关闭,而是从 ESTABLISHED 的状态变成 FIN_WAIT2 并且连接状态没有后续迁移,而对端的连接状态则是从 ESTABLISHED 变成了 CLOSE_WAIT 。
后来发现这和 TCP/IP 栈的4次挥手断开连接有关,列出一些踩坑时的收获。
先看一张 Socket 关闭连接的状态迁移路径图:
在 Client 端调用 socket.close() 时,首先会往对端(即 Server 端)发送一个 FIN 包,接着将自身的状态置为 FIN_WAIT1 ,此时主动关闭端(即 Client 端)处于持续等待接收对端的响应 FIN 包的 ACK 回应状态,此时对端的状态是处于 ESTABLISHED ,一旦收到了 Client 发来的 close 连接请求,就回应一个 FIN 包,表示收到该请求了,并将自身状态置为 CLOSE_WAIT ,这时开始等待 Server 端的应用层向 Client 端发起 close 请求。
这时 Client 端一旦收到 Server 端对第一个 FIN 包的回应 ACK 就会将进入下一个状态 FIN_WAIT_2 来等待 Server 发起断开连接的 FIN 包。在FIN_WAIT_1 的 time_wait 中, Server 端会发起 close 请求,向 Client 端发送 FIN 包,并将自身状态从 CLOSE_WAIT 置为 LAST_ACK ,表示 Server 端的连接资源开始释放了。同时 Client 端正处于 FIN_WAIT2 状态,一旦接收到 Server 端的 FIN 包,则说明 Server 端连接已释放,接着就可以释放自身的连接了,于是进入 TIME_WAIT 状态,开始释放资源,在经过设置的 2个 MSL 时间后,状态最终迁移到 CLOSE 说明连接成功关闭,一次 TCP 4次挥手 关闭连接的过程结束。
通常会出现状态滞留的情况有下面几种:
此外,如果在单台服务器上并且不做负载均衡而处理大量连接的话,可以在 /proc/sys/net/ipv4/ip_local_port_range 中减少端口的极限值,限制每个时间段的最大端口使用数,从而保证服务器的稳定性,一旦出现大量的 TIME_WAIT 阻塞后续连接,是比较致命的。
此外还遇到了另一个小问题,在关闭连接时,一开始用的是 socket.terminate() ,然而 netstat 时却发现大量连接没有释放,后来发现 Python Socket 的 terminate() 只是发送 socket.SHUT_WR 和 socket.SHUT_RD 来关闭通道的读写权限而并没有释放连接句柄。导致了连接已经无法使用,但仍然处于 ESTABLISHED 状态。
解决方法就是使用 socket.close() 来替换 socket.terminate()
后来又看到如果是 DDoS 攻击的话,可能会阻塞住 socket.close() ,导致后续连接未关闭,大量流量进入服务器。
所以比较好的方式是在 socket.close() 之前先调用 socket.terminate() 关闭通道的读写权限,再调用 socket.close()
以上是关于关于关闭 Socket 的一些坑的主要内容,如果未能解决你的问题,请参考以下文章