twisted - 获取操作系统选择的监听端口

10 投票
3 回答
4063 浏览
提问于 2025-04-16 23: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() 创建的端口的信息。因为客户端必须先宣布它的端口,才能让连接发生,所以我不能单纯地等着连接到来。

3 个回答

0

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

>>> serv._getPort()._realPortNumber

否则你也可以这样做:

>>> serv._port._realPortNumber
1

如果你需要处理这些接口,这里是我在本地设置下稍微调整过的实现方式(回调选项在这里也能很好地工作):

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
21

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 其实不会在你用 privilegedStartService 启动它之前调用 listenTCP。而且,IListeningPort 也并不是通过公共接口直接提供的。所以,你需要自己写一个 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)

撰写回答