感谢@rodrigo在评论中的建议,我最终使用了这段代码:
import platform
import re
import subprocess
from typing import Set
if platform.system() == 'Windows':
def _get_ports(pid):
sp = subprocess.run(['netstat', '-anop', 'TCP'],
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
check=True)
rx_socket = re.compile(br'''(?x) ^
\s* TCP
\s+ 127.0.0.1 : (?P<port>\d{1,5})
\s+ .*?
\s+ LISTENING
\s+ (?P<pid>\d+)
\s* $''')
for line in sp.stdout.splitlines():
rxm = rx_socket.match(line)
if rxm is None:
continue
sock_port, sock_pid = map(int, rxm.groups())
if sock_pid == pid:
yield sock_port
else:
def _get_ports(pid):
sp = subprocess.run(['lsof', '-anlPFn', '+w',
f'-p{pid}', '-i4TCP@127.0.0.1', '-sTCP:LISTEN'],
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
check=True)
for line in sp.stdout.splitlines():
if line.startswith(b'n'):
host, port = line.rsplit(b':', 1)
port = int(port)
yield port
def get_ports(pid: int) -> Set[int]:
"""Get set of local-bound listening TCPv4 ports for given process.
:param pid: process ID to inspect
:returns: set of ports
"""
return set(_get_ports(pid))
print(get_ports(12345))
它适用于Linux、macOS和Windows,并查找给定进程处于LISTEN状态的所有本地绑定的TCPv4端口。为了加快速度,它还跳过了所有种类的主机/端口/用户名反向查找,并且不需要提升权限。因此,最后的想法就是让Appium(或其他任何东西)只在
0.0.0.0:0
上启动,它会绑定到操作系统提供的第一个可用端口,然后检查它现在正在侦听哪些端口。没有竞争条件。
EADDRINUSE
错误,就递增端口号并循环直到找到一个可用的? - rodrigo0
成功启动,我可以想到两个解决方案:1. 修补Appium以报告使用的端口(毕竟它是开源的);2. 使用类似lsof -p <pid> -i4 -P -n | grep LISTEN
这样的命令来发现它正在使用的端口。 - rodrigo