python - 如何为子进程选择空闲端口?

标签 python linux macos networking network-programming

我正在围绕 Appium 服务器编写一个 Python 包装器。 Appium 接受要绑定(bind)到的本地端口的命令行参数。不幸的是,Appium 无法为自己自动选择空闲端口,因此它要么绑定(bind)到明确指定的端口,要么失败并返回 EADDRINUSE。即使告诉它绑定(bind)到端口 0,它也会成功启动,但不会显示它绑定(bind)到的端口。

如果我自己在 Python 包装器中找到一个空闲端口,则无法保证在我将其传递给 Appium 的同时其他一些进程不会绑定(bind)到同一端口。如果我自己不先发布它,Appium 将无法绑定(bind)到它,所以我必须这样做。

我知道这在实践中不太可能发生,但是在以跨平台方式(Linux、macOS、Windows)将本地端口号传递给另一个进程之前“保留”本地端口号的“正确方法”是什么? ?

最佳答案

感谢@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 状态的给定进程找出所有本地绑定(bind)的 TCPv4 端口。它还会跳过各种主机/端口/用户名反向查找以使其更快,并且不需要提升权限。

所以,最后,我们的想法是让 Appium(或其他任何东西)在 0.0.0.0:0 上启动,它将自己绑定(bind)到操作系统提供的第一个可用端口,并且然后检查它现在正在监听哪些端口。没有竞争条件。

关于python - 如何为子进程选择空闲端口?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44875422/

相关文章:

macos - NSTableView 在第一行上方有多余的空间

macos - 如何以编程方式确定哪个 Mac 驱动器是启动驱动器?

python - 在 Python 中将 float 与一个非常大的整数相乘

linux - docker : 'go.mod: permission denied' on go commands

python - 如何将 PyQtGraph GraphicView 窗口设置为最大化状态

linux - Bash - 管道多个 grep 并打印输出

java - Java使用的内存多于堆大小(或正确大小的Docker内存限制)

java - 在 OS X 上安装用于 linux 的 jdk

python - 3D 曲面图中数组的 bool 掩码会破坏色彩图

python - 在 Django HTML 模板中使用 {% with %} 标签