【问题标题】:twisted - get OS-chosen listen porttwisted - 获取操作系统选择的监听端口
【发布时间】:2011-08-15 19:49:38
【问题描述】:

我正在使用应用程序框架编写一个扭曲的 P2P 客户端。传入连接的侦听端口将位于随机(操作系统确定的)端口上。但是,我需要一种方法来确定创建后的端口是什么:

import twisted... etc.

application = service.Application('vmesh')
peerservice = MyPeerService()
servicecollection = service.IServiceCollection(application)
factory = MyPeerFactory(peerservice)
server = internet.TCPServer(0, factory) # listen on random port
listen_port = server.getHost().port # ??? doesn't work...
server.setServiceParent(servicecollection)

我在文档中找不到任何关于查询由internet.TCPServer()reactor.listenTCP() 创建的端口并将其转发到的任何内容。我不能简单地等待连接发生,因为客户端必须宣布其端口才能使这些连接发生。

【问题讨论】:

    标签: python tcp twisted p2p


    【解决方案1】:

    listenTCP 返回一个IListeningPort,它有一个getHost() 方法返回一个带有port 的对象。例如:

    >>> from twisted.internet import reactor
    >>> from twisted.internet.protocol import Factory
    >>> port = reactor.listenTCP(0, Factory())
    >>> port.getHost().port
    55791
    

    但是,TCPServer 不会调用 listenTCP,直到它以 privilegedStartService 开头。另外,IListeningPort 实际上并没有通过公共 API 公开。因此,您需要编写自己的Service。幸运的是,这很容易做到。 TCPServer 做的不多。您只需要编写一个在它开始侦听时在某处报告其端口的程序。这是一个例子:

    from twisted.internet import reactor
    from twisted.application.service import Service
    
    class PortReporter(Service, object):
        def __init__(self, factory, reportPort):
            self.factory = factory
            self.reportPort = reportPort
    
        def privilegedStartService(self):
            self.listeningPort = reactor.listenTCP(0, self.factory)
            self.reportPort(self.listeningPort.getHost().port)
            return super(PortReporter, self).privilegedStartService()
    
        def stopService(self):
            self.listeningPort.stopListening()
            return super(PortReporter, self).stopService()
    

    然后您可以在 tac 文件中使用它,如下所示:

    from twisted.internet.protocol import Factory
    from twisted.application.service import Application
    application = Application("test")
    def showPortNumber(n):
        print("The port number is: %d" % (n,))
    PortReporter(Factory(), showPortNumber).setServiceParent(application)
    

    【讨论】:

      【解决方案2】:

      如果您需要使用端点执行此操作,这里是我的实现,对我的本地设置稍作调整(回调选项在这里也可以很好地工作):

      class PortReporter(StreamServerEndpointService, object):
          def __init__(self, endpoint, factory):
              StreamServerEndpointService.__init__(self, endpoint, factory)
          self._reportedPort = None
      
      def privilegedStartService(self):
          r = super(PortReporter, self).privilegedStartService()
          self._waitingForPort.addCallback(self.port_cb)
          return r
      
      def port_cb(self, port):
          self._reportedPort = port.getHost().port
          return port
      
      def getReportedPort(self):
          return self._reportedPort
      

      【讨论】:

      • 当然,这仅在端点用于具有端口的传输类型时才有效。例如,如果您尝试将 PortReporter 与 UNIX 服务器一起使用,则会失败并显示 AttributeError。这似乎很明显,但是如果您使用端点让您的用户完全控制您的应用程序的网络配置,那么您事先不知道他们是否要输入 UNIX 地址...
      • 感谢 exarkun - 绝对需要注意。在我的情况下,我在服务发现的上下文中使用它,其中每个服务都被称为 HTTP,并将主机 + 端口组合报告回服务代理。在 tx 领域还没有真正找到类似的东西..(欢迎提出建议)
      【解决方案3】:

      如果您还没有启动服务器(还没有调用startService),您可以像这样访问绑定到服务器的端口:

      >>> serv._getPort()._realPortNumber
      

      你也可以这样做:

      >>> serv._port._realPortNumber
      

      【讨论】:

      • 这些是私有 API;请不要在 Twisted 中调用以“_”开头的 API。
      • @Glyph :OP 已经说明了一个问题,我给了他一个可能不是 正确 的解决方案,所以如果你知道另一种不涉及调用的方法内部方法请贴出来,如果不好的话毕竟我们都是成年人
      • 我的解决方案提供了所需的方法。而且 Glyphs 的评论没有任何不成熟的地方。
      • @Jakob: 对不起,任何理解失误,当我说“我们在这里都是成年人”时,我引用了关于访问属性的 python 口号 我们在这里都是同意的成年人 我并不是说有什么不好,我非常抱歉:(除此之外,当您关闭临时套接字时,您的方法有一个问题,您绑定的端口将再次空闲,并且有可能(即使概率很小)在您进行更改以将其绑定到您的服务器之前,此操作系统会将其分配给另一个进程。
      • @detly:我不知道,“同意”部分总是让我将其解释为,我们是成年人,不需要通过访问修饰符来保护自己,我们知道何时以及如何打破规则。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-28
      • 1970-01-01
      • 2018-02-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多