删除CLOSS_WAIT套接字连接
1. 概述
在本教程中,我们将学习如何删除CLOSE_WAIT套接字连接。
首先,我们将了解CLOSE_WAIT状态是什么。然后,我们将看到我们可以通过杀死使用它的进程来删除CLOSE_WAIT套接字连接。或者,我们将了解到我们可以关闭与CLOSE_WAIT套接字关联的文件描述符以将其删除。
2. CLOSE_WAIT状态
当我们使用 TCP 协议时,套接字有一个与之关联的状态。例如,当服务器使用套接字并等待端口上的传入连接时,该套接字处于LISTEN状态。
**我们可以使用ss 命令列出所有套接字及其状态。*如果我们添加-ta*选项,ss会列出处于任何状态(a选项)的 TCP 套接字(t选项)。让我们看看结果:
$ ss -ta
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 0.0.0.0:ssh 0.0.0.0:*
LAST-ACK 0 1 192.168.0.5:56466 217.70.190.250:https
ESTAB 0 0 192.168.0.5:51652 31.13.94.52:https
TIME-WAIT 0 0 192.168.0.5:4262 195.201.141.166:https
(...)
我们可以在第一列看到套接字状态。
建立连接后,连接的任何一方都可以结束它。如果远程端结束连接,我们将无法发送或接收更多数据。因此,我们也只能关闭连接。
**当一侧关闭连接时,另一侧的套接字变为CLOSE_WAIT状态。**因此,CLOSE_WAIT状态意味着套接字在远程端已关闭,系统正在等待本地端关闭它。
然后,删除CLOSE_WAIT套接字连接的唯一方法是关闭它。通常,进程知道远程端何时关闭连接,本地进程将其关闭。这是正常行为,因此很少看到CLOSE_WAIT套接字。
虽然,如果我们看到一个CLOSE_WAIT套接字,可能是因为软件错误。例如,如果进程变得无响应并且远程端关闭了连接,则套接字永远不会关闭。因此,如果发生这种情况,套接字将保持CLOSE_WAIT状态。
3. 使用CLOSE_WAIT套接字查找进程
首先,我们必须找到带有CLOSE_WAIT套接字的进程。我们可以使用带有-tap标志的ss*命令来查看与每个套接字关联的进程。* p标志告诉ss打印每个套接字的进程信息。
我们可以使用*grep 过滤CLOSE_WAIT套接字。但是,我们应该通过CLOSE-WAIT而不是CLOSE_WAIT*进行过滤。让我们尝试一下:
$ ss -tap | grep CLOSE-WAIT
CLOSE-WAIT 1 0 127.0.0.1:9999 127.0.0.1:56990 users:(("nc",pid=23117,fd=4)
有一个PID 为 23117 的nc 进程,并且套接字处于CLOSE_WAIT状态。
或者,我们可以使用state参数按 TCP 状态过滤套接字。因此,我们可以添加参数state CLOSE-WAIT来列出所有CLOSE_WAIT套接字。让我们尝试一下:
$ ss -tap state CLOSE-WAIT
Recv-Q Send-Q Local Address:Port Peer Address:Port Process
1 0 127.0.0.1:9999 127.0.0.1:56990 users:(("nc",pid=23117,fd=4))
我们可以注意到ss命令列出了与之前相同的nc进程**。
4. 移除CLOSE_WAIT Socket 连接
现在我们知道了这个过程,我们可以使用两种方法之一来删除CLOSE_WAIT套接字。一种方法是终止进程。但是,有时,我们不想终止该进程。因此,在这些情况下,我们可以使用另一种方法,即关闭文件描述符。
4.1. 杀死进程
删除CLOSE_WAIT套接字的一种方法是终止进程。当进程终止时,系统也会关闭它的所有套接字。
我们可以使用kill 命令和进程 ID 来杀死它。我们可以尝试使用默认的SIGTERM信号来杀死它。但是,进程可能没有响应,所以如果进程没有被杀死,我们可以使用SIGKILL信号重试。
让我们运行kill以终止PID 23117的nc进程:
$ kill 23117
现在,让我们检查套接字是否仍在CLOSE_WAIT中:
$ ss -tap state CLOSE-WAIT
Recv-Q Send-Q Local Address:Port Peer Address:Port Process
1 0 127.0.0.1:9999 127.0.0.1:56990 users:(("nc",pid=23117,fd=4))
我们可以看到socket还在,可能是nc进程没有响应的缘故。因此,让我们尝试使用SIGKILL信号强制终止它:
$ kill -KILL 23117
现在,让我们再次检查是否有任何CLOSE_WAIT套接字:
$ ss -tap state CLOSE-WAIT
Recv-Q Send-Q Local Address:Port Peer Address:Port Process
最后,我们可以看到没有任何CLOSE_WAIT套接字。
4.2. 关闭文件描述符
删除CLOSE_WAIT套接字的另一种方法是关闭关联的文件描述符。为此,我们可以使用gdb 调试器。
当我们不想终止进程时,我们可以使用这种替代方法。但是,我们将操纵流程执行并更改其内部状态。因此,在我们关闭文件描述符后,进程可能会出现错误行为,继续正常执行,或者退出。
在上一节中,我们发现了一个带有CLOSE_WAIT套接字和 PID 23117 的进程。在 PID 之后,ss打印“fd=4”,这意味着与该套接字关联的文件描述符是数字 4。
*现在,我们可以使用gdb关闭文件描述符。首先,我们可以使用-p标志和 PID将gdb*附加到进程。然后,我们可以通过在 gdb提示符下执行print (int)close(FD)命令来关闭文件描述符。**我们必须将FD替换为与CLOSE_WAIT套接字关联的文件描述符编号。最后,我们退出gdb,执行quit命令。
让我们附加到具有 PID 23117 的进程并关闭其文件描述符编号 4:
$ gdb -p 23117
GNU gdb (GDB) 11.2
Attaching to process 23117
(gdb) print (int)close(4)
$1 = 0
(gdb) quit
**或者,我们可以附加到进程,关闭 FD,然后使用单行退出gdb 。*我们添加-batch参数和-ex参数,然后在引号内添加print (int)close(4)*命令。让我们尝试一下:
$ gdb -p 23117 -batch -ex 'print (int)close(4)'
0x00007f99d1a0fb84 in select () from /lib64/libc.so.6
$1 = 0
[Inferior 1 (process 23117) detached]
让我们重新运行ss 看看是否有任何CLOSE_WAIT套接字:
$ ss -tap state CLOSE-WAIT
Recv-Q Send-Q Local Address:Port Peer Address:Port Process
我们可以看到现在没有任何CLOSE_WAIT套接字。