Contents

删除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套接字。