【问题标题】:get open TCP port in Python [duplicate]在Python中获取打开的TCP端口[重复]
【发布时间】:2011-02-19 18:17:32
【问题描述】:

我想在 Python 中获取本地主机上的任何随机打开的 TCP 端口。最简单的方法是什么?

【问题讨论】:

  • 这就像狗追车——你得到一辆后要做什么?
  • 我需要另一个控制台应用程序的 TCP 端口号,该应用程序需要一个端口作为它将监听的参数。
  • 您正在考虑的方法存在明显的(尽管是次要的)竞争条件。当您将其传递给另一个进程时,该端口可能会被占用。您是否拥有其他进程的代码?如果是这样,最好让它获取一个随机端口并将其报告给第一个进程。
  • 是的,这就是我所说的“不是 100% 正确”的意思。但是在我得到端口和调用另一个进程之间总会有这么短的时间跨度。 (而且我没有那个代码。)

标签: python tcp tcp-port


【解决方案1】:

我目前的解决方案:

def get_open_port():
        import socket
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.bind(("",0))
        s.listen(1)
        port = s.getsockname()[1]
        s.close()
        return port

不是很好,也不是 100% 正确,但现在可以。

【讨论】:

  • 为什么调用listen()?对我来说,只要我调用 bind() 就会填充 s.getsockname()[1]
  • 在这种情况下是否需要在s.close() 之前调用s.shutdown()
  • fwiw,这与流行的节点包get-port 使用的方法相同。 github.com/sindresorhus/get-port/blob/master/index.js
【解决方案2】:

可以通过将套接字绑定到操作系统选择的端口来找到空闲端口。在操作系统选择了一个端口后,就可以对socket进行处理了。但是,此解决方案无法抵抗竞争条件 - 在获取空闲端口号和使用此端口之间的短时间内,其他进程可能会使用此端口。

import socket

def find_free_port():
    with socket.socket() as s:
        s.bind(('', 0))            # Bind to a free port provided by the host.
        return s.getsockname()[1]  # Return the port number assigned.

https://godbolt.org/z/fs13K13dG

【讨论】:

  • 有什么原因你没有在这个函数中调用 s.close() 吗?当我不调用 s.close() 时,我会收到 ResourceWarning: unclosed <socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('0.0.0.0', 39869)>
  • @jstm,你是对的,我更新了代码。
【解决方案3】:

我实际上在我的一个程序中使用了以下内容:

port = random.randint(10000,60000)

当然,这比您拥有的代码更容易发生冲突。但我从来没有遇到过问题。关键是,在任何给定时间,这些高编号端口中的大多数都未使用,如果您只是随机选择一个,那么与另一个进程发生冲突的可能性很小。如果您执行类似于您在答案中发布的解决方案(打开套接字并获取其端口号),则几乎可以肯定该端口不会发生冲突。因此,如果这只是您自己使用的东西(而不是您要向公众发布的东西),请考虑是否值得提出一个真正的防弹解决方案。很可能它永远不会有所作为。

受 Marcelo Cantos 对您的问题的评论的启发,我将补充说,在这种情况下,标准解决方案是将使用端口的进程绑定到它,然后与需要它的任何其他程序共享该信息.通常它会做一些事情,比如将一个包含端口号的临时文件写入文件系统中的某个标准位置。由于您正在使用的过程不会这样做,因此在某种意义上,您提出的任何解决方案都将是一个小技巧。但同样,如果它只是供您自己使用,那可能没问题。

【讨论】:

    【解决方案4】:

    临时端口基本上位于 49152 - 65535 范围内。 如果你想检查更大范围的端口,那么只需更改 randint 中的值。

    import pustil
    from random import randint
    def getfreeport():
        port = randint(49152,65535)
        portsinuse=[]
        while True:
            conns = pstuil.net_connections()
            for conn in conns:
                portsinuse.append(con.laddr[1])
            if port in portsinuse:
                port = randint(49152,65535)
            else:
                break
        return port
    

    【讨论】:

      【解决方案5】:

      这是我的版本,但是如果你指定一个端口范围,它并不是随机的。这也会受到竞争条件的影响,但如果您需要提前了解端口,这是我知道的最好方法。

      import socket
      import errno
      import contextlib
      
      reserved_ports = set()
      
      def get_open_port(lowest_port = 0, highest_port = None, bind_address = '', *socket_args, **socket_kwargs):
          if highest_port is None:
              highest_port = lowest_port + 100
          while lowest_port < highest_port:
              if lowest_port not in reserved_ports:
                  try:
                      with contextlib.closing(socket.socket(*socket_args, **socket_kwargs)) as my_socket:
                          my_socket.bind((bind_address, lowest_port))
                          this_port = my_socket.getsockname()[1]
                          reserved_ports.add(this_port)
                          return this_port
                  except socket.error as error:
                      if not error.errno == errno.EADDRINUSE:
                          raise
                      assert not lowest_port == 0
                      reserved_ports.add(lowest_port)
              lowest_port += 1
          raise Exception('Could not find open port')
      

      【讨论】:

      • 不幸的是,这不是 100% 防故障的,并且当端口确实被另一个应用程序使用时可能会返回 True。我还没有确定原因,但是当另一个进程已经在监听端口时,.bind() 不会总是失败。
      • @Joe 是否有可能其他应用程序绑定到不同的地址但具有相同的端口?还是不同的地址族?
      • 有可能。我所知道的是,在我的情况下,我试图启动的服务器应用程序无法打开端口,即使上面的代码说端口是空闲的。另一个应用程序是 erl.exe (erlang),如果你想自己尝试的话,我是通过 RabbitMQ 的设置获得的。
      • 我喜欢使用 contextlib,我没见过
      猜你喜欢
      • 1970-01-01
      • 2012-02-12
      • 1970-01-01
      • 2012-10-17
      • 1970-01-01
      • 2018-12-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多