I am writing a twisted P2P client using the application framework. The listen port for incoming connections will be on a random (OS-determined) port. However, I need a way to determine what that port is after creating it:
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)
I can't find anything in the docs about querying the port created by internet.TCPServer() or by reactor.listenTCP() which it forwards to. I can't simply wait for a connection to occur since the client has to announce its port in order for those connections to ever happen.
listenTCP returns an IListeningPort, which has a getHost() method that gives back an object with a port.  For example:
>>> from twisted.internet import reactor
>>> from twisted.internet.protocol import Factory
>>> port = reactor.listenTCP(0, Factory())
>>> port.getHost().port
55791
However, TCPServer doesn't call listenTCP until it is started with privilegedStartService.  Plus, the IListeningPort isn't actually exposed via a public API.  So, you will need to write your own Service.  Luckily, it's quite easy to do this; TCPServer doesn't do very much.  You just need to write one that reports its port somewhere as soon as it starts listening.  Here's an example:
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()
You can then use this in a tac file, like so:
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)
FWIW if you need to do this with endpoints here is my implementation with a slight tweak for my local setup (the callback option would work well here too):
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With