在OS X上释放死进程使用的UDP端口

时间:2016-11-09 17:00:32

标签: macos sockets osx-elcapitan

我在OS X 10.11.6上尝试运行一个通常在启动时侦听UDP端口8008的程序。

该程序通常还会在其操作期间生成几个辅助子进程,但该端口由父进程绑定。

不幸的是,退出程序时,即使程序(父级+子级)不再存在,端口仍然会保持打开状态。

当发生这种情况时,如果我再次尝试运行该程序,它会自然失败并出现EADDRINUSE错误,在这些情况下无论我尝试什么,我找到的唯一解决方案是重启机器。

我很难相信我不能在没有重启的情况下释放端口。

以下是我到目前为止运行的一些诊断程序(我使用和不使用sudo运行所有这些诊断程序):

使用8008端口lsof查找流程:

$ lsof -i -n -P | grep UDP | grep 8008

但令人惊讶的是,没有任何结果。

但是,我对netstat

有更多好运
$ netstat -tulnvp udp | grep 8008
udp4  0  0  *.8008    *.*    196724   9216  47205   0

所以,端口确实是绑定的,罪魁祸首是pid 47205,但是:

$ ps aux | grep 47205

不会返回任何内容。对于PID 4720647207(同样是分配给子节点的PID)也是如此。我还尝试了grep的其他变体(程序名称,路径等)。

我还查找了报告47205作为其父级的任何流程:

$ ps -axo pid,ppid,command | grep 47205

所以孩子们的过程也显然已经死了。

无法kill任何内容,我试图大肆宣传launchd,希望它可以删除任何僵尸子进程:

$ sudo kill HUP 1
$ sudo kill -s HUP 1

但是,唉,netstat仍然显示端口绑定。

最后,我尝试重新启动环回接口:

$ sudo ifconfig lo down
$ sudo ifconfig lo up

但是,再一次,没有效果。

自程序上次运行以来,我已经等了几个小时,所以我很确定现在已经发生任何超时,但是端口才刚刚被释放。

有关如何在不重启的情况下强制释放端口的任何想法?

编辑:

  • 有问题的程序是电子封装的Patchwork
  • 此问题源于此github issue
  • 虽然找到一个可以防止问题首先出现的解决方案/错误修复是理想的,但我也对从终端手动关闭该端口的方法感兴趣

5 个答案:

答案 0 :(得分:3)

在您的代码中,在创建套接字之后,但在bind调用之前,请调用以下内容:

int val = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));

然后致电bind。即使端口正在使用,上面的内容也允许socket绑定成功。

在同一端口上尝试recvfrom的两个进程将导致其中一个进程接收数据包,而不是另一个进程。而且这不是确定性的。因此,请确保您实际上没有合法运行和共享端口的两个进程。

答案 1 :(得分:1)

确实可以手动关闭端口,无需重新启动机器。在各种Linux风格上,这通常是通过伪装作为进程发出系统调用来完成的(例如,套接字文件描述符上的close(fd)系统调用)。

过程:

  • 打开UDP端口:netcat -u 127.0.0.1 33333
  • 检查UDP端口:netstat -npu (u for UDP),它将为您提供占用该端口的PID。
  • 运行:lsof -np $pid获取该PID以获取套接字的文件描述符。
  • 然后为该PID运行GDB:sudo gdb -p 73599
  • 在GDB内部运行call close(file_descriptor)

示例:

COMMAND   PID  USER   FD   TYPE   DEVICE SIZE/OFF     NODE NAME
netcat  73599 ubunt  cwd    DIR  259,2     4096 13895497 /home/ubunt/Downloads
netcat  73599 ubunt  rtd    DIR  259,2     4096        2 /
netcat  73599 ubunt  txt    REG  259,2    31248 28835938 /bin/nc.openbsd
netcat  73599 ubunt  mem    REG  259,2    47600 23990813 /lib/x86_64-linux-gnu/libnss_files-2.23.so
netcat  73599 ubunt  mem    REG  259,2  1868984 23990714 /lib/x86_64-linux-gnu/libc-2.23.so
netcat  73599 ubunt  mem    REG  259,2   101200 23990866 /lib/x86_64-linux-gnu/libresolv-2.23.so
netcat  73599 ubunt  mem    REG  259,2    81040 23990710 /lib/x86_64-linux-gnu/libbsd.so.0.8.2
netcat  73599 ubunt  mem    REG  259,2   162632 23990686 /lib/x86_64-linux-gnu/ld-2.23.so
netcat  73599 ubunt    0u   CHR 136,19      0t0       22 /dev/pts/19
netcat  73599 ubunt    1u   CHR 136,19      0t0       22 /dev/pts/19
netcat  73599 ubunt    2u   CHR 136,19      0t0       22 /dev/pts/19
netcat  73599 ubunt    3u  IPv4 22142418    0t0      UDP 127.0.0.1:45255->127.0.0.1:33333

然后是GDB:

$sudo gdb -p 73599
...
(gdb) call close(3u)
$1 = 0

您将看到该端口不再存在:

ubunt@ubunt-MS-7A94:~$ lsof -np 73599
COMMAND   PID  USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
netcat  73599 ubunt  cwd    DIR  259,2     4096 13895497 /home/ubunt/Downloads
netcat  73599 ubunt  rtd    DIR  259,2     4096        2 /
netcat  73599 ubunt  txt    REG  259,2    31248 28835938 /bin/nc.openbsd
netcat  73599 ubunt  mem    REG  259,2    47600 23990813 /lib/x86_64-linux-gnu/libnss_files-2.23.so
netcat  73599 ubunt  mem    REG  259,2  1868984 23990714 /lib/x86_64-linux-gnu/libc-2.23.so
netcat  73599 ubunt  mem    REG  259,2   101200 23990866 /lib/x86_64-linux-gnu/libresolv-2.23.so
netcat  73599 ubunt  mem    REG  259,2    81040 23990710 /lib/x86_64-linux-gnu/libbsd.so.0.8.2
netcat  73599 ubunt  mem    REG  259,2   162632 23990686 /lib/x86_64-linux-gnu/ld-2.23.so
netcat  73599 ubunt    0u   CHR 136,19      0t0       22 /dev/pts/19
netcat  73599 ubunt    1u   CHR 136,19      0t0       22 /dev/pts/19
netcat  73599 ubunt    2u   CHR 136,19      0t0       22 /dev/pts/19

GDB可用于MacOS,因此它也适用于您的情况。

答案 2 :(得分:1)

系统可以保持套接字打开,直到I / O进程仍在进行中。即使进程死亡但未明确关闭套接字。如果你的插座没有在几小时关闭,那么你可能会错过一些东西。尝试使用低级内核调查而不是像netstat或lsof这样的顶级实用程序。

<强>声明

我不是OS X专家,也不是linux的大多数命令。如果其他人有同样的问题,我仍然留在那里。

<强> 1。尝试查看套接字是否仍然存活(可选)

我可能会建议检查套接字通信。

 tcpdump -A -s0 port 8080  and tcpdump -A -s0 -ilo port 8080

如果您看到任何通过套接字传输的数据,您可以确保该进程处于活动状态。或者可能是它的孩子之一。之后你可以用strace

来抓住pid

<强> 2。检查流程及其状态

Linux有很棒的 procfs 。你可以从那里得到很多东西。确保你可以看到所有打开的文件描述符

ls -al  /proc/47205/fd

如果您看到输出并且 / proc / 47205 存在,则不会发布pid ps 。您将看到所有打开的文件及其fds。它看起来像

  

133 - &gt;插座:[32242509]

其中133是fd编号

不幸的是OS X没有/ proc文件系统。我找到的替代命令。

procexp 47205 fds

但我不确定它100%正常工作。

第3。在另一个进程中关闭文件描述符(套接字)

在linux中有一个很好的命令

fuser -k -n udp 8080

这将显式关闭阻塞端口的所有进程。似乎OS X may have fuser too

另一个真正的黑客方法是使用gdb连接进程并在进程内运行命令,因为文件描述符号仅在进程环境中有效,正如@MindaugasBernatavičius写道:

gdb -p 47205
>call shutdown([fd_number],2)
>call close([fd_number])

有第三种方式,如果可能,您可以重新启动整个网络。请注意,向下和向上只是环回接口是不够的。在linux下运行

systemctl restart network  

4.如何防止插座卡在系统中

在程序退出之前,应始终确保已关闭socked。 I seen many issues with nodejs套接字保持打开状态。调用Socket.destroy()将解决问题

在退出应用程序之前,可以将套接字销毁代码放在此处:

  

app.on('close',function(code){

     

//用户关闭了应用。杀死主持人进程。

     

process.exit();

     

});

答案 3 :(得分:0)

您的问题类似于:

正如你所说:

  

最后,我尝试重新启动环回接口:

     

$ sudo ifconfig lo down

     

$ sudo ifconfig lo up

您是否尝试重新启动所有可用的网络接口(lan或wlan)而不仅仅是环回?

您可以使用原生MacOS命令实用程序(来自here)代替ifconfig,然后关闭电源,然后启动设备本身(将en0调整为your device name):

networksetup -setairportpower en0 off
networksetup -setairportpower en0 on

您最后还可以尝试使用以下命令发布和续订DHCP:

sudo dhclient -v -r

此致

答案 4 :(得分:0)

一个相关问题:mac改变了SO_REUSEADDR的行为和SO_REUSEPORT:

Behavior of SO_REUSEADDR and SO_REUSEPORT changed?

我是iptux [1]的维护者,如果我使用SO_REUSEPORT,程序可以启动,但是我无法从这个端口收到消息,所有消息都作为黑洞进入未关闭的端口。 / p>

[1] https://github.com/iptux-src/iptux