Search code examples
pythonasynchronousnetwork-programmingclienttwisted

Twisted client for a send only protocol that is tolerant of disconnects


I've decided to dip my toe into the world of asynchronous python with the help of twisted. I've implemented some of the examples from the documentation, but I'm having a difficult time finding an example of the, very simple, client I'm trying to write.

In short I'd like a client which establishes a tcp connection with a server and then sends simple "\n" terminated string messages off of a queue object to the server. The server doesn't ever respond with any messages so my client is fully unidirectional. I /think/ that what I want is some combination of this example and the twisted.internet.protocols.basic.LineReceiver convenience protocol. This feels like it should be just about the simplest thing one could do in twisted, but none of the documentation or examples I've seen online seem to fit quite right.


Solution

  • What I have done is not used a Queue but I am illustrating the code that sends a line, once a connection is made. There are bunch of print stuff that will help you understand on what is going on.

    Usual import stuff:

    from twisted.web import proxy
    from twisted.internet import reactor
    from twisted.internet import protocol
    from twisted.internet.protocol import ReconnectingClientFactory 
    from twisted.protocols import basic
    from twisted.python import log
    import sys
    log.startLogging(sys.stdout)
    

    You create a protocol derived from line receiver, set the delimiter. In this case, I simply write a string "www" once the connection is made. The key thing is to look at protocol interface at twisted.internet.interface.py and understand the various methods of protocol and what they do and when they are called.

    class MyProtocol(basic.LineReceiver):
    
        #def makeConnection(self, transport):
        #    print transport       
    
        def connectionLost(self, reason):
            print reason
            self.sendData = False
    
        def connectionMade(self):
            print "connection made"
            self.delimiter = "\n"
            self.sendData = True
            print self.transport
            self.sendFromQueue()
    
        def sendFromQueue(self):
            while self.sendData:
                msg = dataQueue.get()
                self.sendLine(msg)
                # you need to handle empty queue
                # Have another function to resume 
    

    Finally, A protocol factory that will create a protocol instance for every connection. Look at method : buildProtcol.

    class myProtocolFactory():
        protocol = MyProtocol
    
        def doStart(self):
            pass
    
        def startedConnecting(self, connectorInstance):
            print connectorInstance
    
        def buildProtocol(self, address):
            print address
            return self.protocol()
    
        def clientConnectionLost(self, connection, reason):
            print reason
            print connection
    
        def clientConnectionFailed(self, connection, reason):
            print connection
            print reason
    
        def doStop(self):
            pass
    

    Now you use a connector to make a connection:

    reactor.connectTCP('localhost', 50000, myProtocolFactory())
    reactor.run()
    

    I ran this and connected it to an server that simply prints what it receives and hence send no ack back. Here is the output:

    1286906080.08   82     INFO 140735087148064 __main__ conn_made: client_address=127.0.0.1:50277
    1286906080.08   83    DEBUG 140735087148064 __main__ created handler; waiting for loop
    1286906080.08   83    DEBUG 140735087148064 __main__ handle_read
    1286906080.08   83    DEBUG 140735087148064 __main__ after recv
    'www\n'
    
    Recieved: 4
    

    The above example if not fault tolerant. To reconnect , when a connection is lost, you can derive your protocol factory from an existing twisted class - ReconnectingClientFactory. Twisted has almost all the tools that you would need :)

    class myProtocolFactory(ReconnectingClientFactory):
        protocol = MyProtocol
    
        def buildProtocol(self, address):
            print address
            return self.protocol()
    

    For further reference

    I suggest that you read : http://krondo.com/?page_id=1327

    [Edited: As per comment below]