当 lsof(1) 报告未使用时,如何在 OS X 上释放端口

当 lsof(1) 报告未使用时,如何在 OS X 上释放端口

我是一名在 OS X 上使用 Eclipse 的开发人员。我一直在努力修复 Web 应用程序中的一个错误,该错误导致在重置 Tomcat 实例时出现一些非常奇怪的行为,导致在强制关闭并重新启动服务器时出现以下错误消息(它将不是干净地关闭,可能是因为我试图修复错误,所以我需要kill它,kill -9 <pid>Eclipse 的内部“强制终止”都给出相同的行为):

本地主机上的 Tomcat v8.0 服务器所需的几个端口(23432、34543)已被使用。该服务器可能已在另一个进程中运行,或者系统进程可能正在使用该端口。要启动此服务器,您需要停止其他进程或更改端口号。

这很好,我已经研究这个东西很长时间了,通常这意味着 Tomcat 仍然在某处运行,只需要将其删除,一切就可以正常工作。

lsof(1)然而,不是列出占用该端口的所有活动进程:

user@yosemite ~ %  sudo lsof -Pan -i tcp -i udp | grep 23432
COMMAND    PID           USER   FD   TYPE             DEVICE SIZE/OFF   NODE NAME
launchd      1           root   23u  IPv6 0x57073763bfdd9c27      0t0    TCP *:5900 (LISTEN)
launchd      1           root   26u  IPv4 0x57073763bfddfb77      0t0    TCP *:5900 (LISTEN)
launchd      1           root   30u  IPv6 0x57073763bfdd9727      0t0    TCP [::1]:631 (LISTEN)
launchd      1           root   31u  IPv6 0x57073763bfdd9c27      0t0    TCP *:5900 (LISTEN)
launchd      1           root   32u  IPv4 0x57073763bfddf2a7      0t0    TCP 127.0.0.1:631 (LISTEN)
launchd      1           root   34u  IPv6 0x57073763bfdd9227      0t0    TCP *:22 (LISTEN)
launchd      1           root   37u  IPv4 0x57073763bfdde9d7      0t0    TCP *:22 (LISTEN)
launchd      1           root   41u  IPv4 0x57073763bfddfb77      0t0    TCP *:5900 (LISTEN)
launchd      1           root   47u  IPv4 0x57073763bfddf2a7      0t0    TCP 127.0.0.1:631 (LISTEN)
...
user@yosemite ~ %  sudo lsof -Pan -i tcp -i udp | grep 23432

我已经通过一个小的 Python 脚本验证了端口确实正在使用,只是为了确保 Eclipse 没有弄错:

user@yosemite ~ % cat socket_open.py
#!/usr/bin/env python
import sys, traceback, socket

HOST=''
PORT = int(sys.argv[1])

sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print "Attempting to bind '%s':%s" % (HOST, PORT)
try:
    sck.bind((HOST, PORT))
    print sck.getsockname()
except Exception as exc:
    traceback.print_exc()

很简单,只需尝试绑定到相同的端口,如果不奏效则抛出异常。

user@yosemite ~ % sudo python socket_open.py 23432
Password:
Attempting to bind '':23432
Traceback (most recent call last):
  File "socket_open.py", line 10, in <module>
    sck.bind((HOST, PORT))
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py", line 224, in meth
    return getattr(self._sock,name)(*args)
error: [Errno 48] Address already in use

那里一个僵尸(java)进程四处游荡,带有我最初杀死的 Tomcat 的 PID,我怀疑这个家伙与它有关:

user@yosemite ~ % ps wwwaux | grep java | grep -v 'grep'
user          975   0.0  0.0        0      0   ??  ?E   11:15AM   0:00.00 (java)

有没有任何我怎样才能释放正在使用的端口而无需重新启动或注销?

每次我重现我的错误时都会发生这种情况,并且每次调试时都必须停止并重新启动,这会浪费大量时间。

在 Linux 上我可能会运行gdb -p PIDclose(<fdnum>)但在 OS XI 上甚至找不到引用该地址的文件描述符(如果有的话)。

答案1

我想这netstat也不起作用吧?

我在 Linux 上也遇到过同样的问题,但那是很久以前的事情了(我确信当时的内核是 2.0.36)。端口已挂在我的 上eth1。我所做的就是禁用网络适配器,然后在几秒钟后重新启用它。这“清除”了使端口处于“半开”状态的内核结构。

我不知道您是否可以down在环回接口上发出并使其工作,但也许值得一试。

您可以尝试发送 SIGCHLDlaunchd并查看会发生什么。或者甚至发送 SIGHUP 来告诉它重新启动并(可能)收获僵尸子进程(kill -HUP 1kill -s HUP 1)。

另一个可以尝试的技巧是使用启动脚本“包装”Tomcat 实例,该脚本将运行真正的 Tomcat 并等待它关闭。然后,您可以看到包装器脚本本身被杀死时会发生什么,或者您可以尝试监听恶意 java 进程以确保它不会变成僵尸进程。

相关内容