0

I have a simple client / server setup. Here's the client code:

from twisted.internet import reactor
from twisted.internet import protocol
from twisted.internet.endpoints import TCP4ClientEndpoint

class MyProtocol(protocol.Protocol):

    def connectionMade(self):
        print "Hello!"

    def dataReceived(self, data):
        print data

class MyProtocolFactory(protocol.ClientFactory):

    def startedConnecting(self, connector):
        print "Starting to connect!"

    def buildProtocol(self, addr):
        return MyProtocol()

    def clientConnectionLost(self, connector, reason):
        print "Lost connection, reason = %s" % reason

    def clientConnectionFailed(self, connector, reason):
        print "Connection failed, reason = %s" % reason
        reactor.stop()

endpoint = TCP4ClientEndpoint(reactor, "127.0.0.1", 54321, timeout=5)
endpoint.connect(MyProtocolFactory())
reactor.run()

For some reason, this client will connect to the server and the protocol works correctly (I can see "Hello!" printed, along with data sent by the server upon a successful connection), but it won't call any of the protocol factory methods. startedConnecting doesn't get called, and if I stop the server, I don't see clientConnectionLost get called. If I try to run the client before the server has started, I would also expect to see clientConnectionFailed get called.

Here's the strange part...If I change the last 3 lines in the code above to the following:

reactor.connectTCP("127.0.0.1", 54321, MyProtocolFactory())
reactor.run()

Then everything works as expected, and all methods get called in all of the cases outlined above.

My understanding of endpoints is that they wrap "connectTCP" (among others) with additional behaviour, but I can't figure out why it works in the second code snippet, but not the first.

Any ideas?

Novark
  • 419
  • 4
  • 15

2 Answers2

1

The client endpoint interface does not call the extra connection-state notification methods of ClientFactory.

So, while endpoints do in some sense "wrap" connectTCP et al, it's not true that they have the exact same behavior as using those lower-level methods.

With endpoints, the factory's job is to provide protocol instances. The factory is no longer responsible for other aspects of connection management.

Jean-Paul Calderone
  • 47,755
  • 6
  • 94
  • 122
  • Regarding that last sentence, "With endpoints [...] instances. The factory [...] management." From a design perspective, why does the behaviour of the factory change depending on whether or not endpoints are used? For some reason, that feels very un-intuitive. If it's no longer the factory's responsibility, then where does that functionality come from when endpoints are used? – Novark Jan 03 '17 at 15:53
  • As I recall, the thinking was that the factory interface wasn't very good and it would be better to take advantage of the new endpoint interface to help shift users away from it. The replacement functionality may depend on exactly what you hoped to achieve with the factory methods. https://twistedmatrix.com/documents/16.4.1/api/twisted.application.internet.ClientService.html covers some of the use cases (roughly those previously handled by ReconnectingClientFactory). – Jean-Paul Calderone Jan 03 '17 at 18:41
  • Thanks for the info. Are you aware of any documentation that outlines this replacement functionality and / or the overall architecture? API docs are one thing, but it would be great to get a summary of how this stuff is supposed to be used and fit together. – Novark Jan 04 '17 at 04:30
  • The prose/long form/howto document for endpoints is at http://twistedmatrix.com/documents/current/core/howto/endpoints.html - afaik, this is the closest thing to what you're asking for (though it may miss the mark). If you think there's some important high-level information that it is missing, I'd encourage you to file a ticket describing the questions you think it doesn't answer. – Jean-Paul Calderone Jan 05 '17 at 13:10
  • I think the endpoint docs will suffice for now. Maybe it's just because I'm still fairly new to twisted, and learning all of its intricacies, but I think it would be pretty swell if someone made a high-level system architecture diagram with blocks and arrows showing how everything is connected. Maybe just wishful thinking on my part though :-). In any case, thanks for the help and resources; it should be enough to get me going. – Novark Jan 05 '17 at 13:40
0

Additional note to supplement my discussion above:

If you’ve used ClientFactory before, keep in mind that the connect method takes a Factory, not a ClientFactory. Even if you pass a ClientFactory to endpoint.connect, its clientConnectionFailed and clientConnectionLost methods will not be called. In particular, clients that extend ReconnectingClientFactory won’t reconnect. The next section describes how to set up reconnecting clients on endpoints.

From the endpoint docs found here: http://twistedmatrix.com/documents/current/core/howto/endpoints.html

Novark
  • 419
  • 4
  • 15