Introduction to Twisted Framework

Twisted is an event-driven networking engine written in Python. A lot of information that covers a lot of different topics can be viewd at the project's website . This post shows a basic example based on the official documentation.

Time UUID server example

Let's suppose that we have a distribuited system, with more than one application server that needs to insert records on a database cluster that is shared for the entire system, the primary key of some tables in this database are Universally unique identifier UUID type this keys has to be unique so we decided develop a service that will return a serial time UUID that will be sequencially different for every request as is shown in the next figure:

Writing The Server

The Protocol

The main goal is implement a network protocol parsing and handling for TCP servers. This protocol will be a subclass of twisted.internet.protocol.Protocol. An instance of the protocol class is instantiated per-connection, on demand, and will go away when the connection is finished. This means that persistent configuration is not saved in the Protocol.

    from twisted.internet import reactor
    from twisted.internet.protocol import Factory
    from twisted.protocols.basic import LineReceiver

    import datetime, time_uuid

    class TimeUUId(LineReceiver):
        def __init__(self):
            self.options = {'C': self.handle_GETUUID, 'X': self.handle_CLOSE}

        def connectionMade(self):
            message = """
                      Welcome to Time UUID generator :-)\r\n
                      press C to get a time UUID
                      press X to exit
                      """
            self.transport.write(message)

        def lineReceived(self, line):
            if line.upper() in self.options:
                self.options[line.upper()]()
            else:
                self.transport.write('Invalid option :-( \n')

        def handle_CLOSE(self):
            self.transport.write('good bye :-) \n')
            self.transport.loseConnection()

        def handle_GETUUID(self):
            uuid =time_uuid.TimeUUID.with_utc(datetime.datetime.utcnow())
            self.transport.write(str(uuid))

The Factory

A design goal must be the possibility to expose the service on multiple ports or network addresses. That's the reason why configuration will be defined using a Factory class that will be a child from twisted.internet.protocol.Factory. The buildProtocol method of the Factory is used to create a Protocol for each new connection.

    class TimeUUIdFactory(Factory):

        protocol = TimeUUId

        def __init__(self):
            pass

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

Starting the Server

    reactor.listenTCP(1320, TimeUUIdFactory())
    reactor.run()

Testing the Server

    $ telnet 127.0.0.1 1320
    Trying 127.0.0.1...
    Connected to 127.0.0.1.
    Escape character is '^]'.

                      Welcome to Time UUID generator :-)

                      press C to get a time UUID
                      press X to exit
                      c
    ecaa6a3a-35ea-11e3-918a-002185c69ff8
    c
    ee078390-35ea-11e3-9cf9-002185c69ff8
    f
    Invalid option :-( 
    x
    good bye :-) 
    Connection closed by foreign host.

Writing the client

The protocol

The class that implents the protocol parsing and hadling is the protocol class, which is a subclass of twisted.internet.protocol.Protocol

    from twisted.internet import reactor
    from twisted.internet.protocol import ClientFactory
    from twisted.protocols.basic import LineReceiver

    class TimeUUIdClient(LineReceiver):

        welcome = 'Welcome to Time UUID generator :-)'

        def send_option(self, option):
            self.sendLine(option.replace(' ', ''))

        def ask_option(self):
            print 'press C to get a time UUID or X to exit\n'
            option = raw_input()
            if option.upper() == 'C':
                self.send_option(option.upper())
            elif option.upper() == 'X':
                print 'good bye :-)\n'
                self.transport.loseConnection()
            else:
                print 'invalid option good bye :-(\n'
                self.transport.loseConnection()

        def connectionMade(self):
            self.status = self.option_status['connected']

        def dataReceived(self, line):
            if self.welcome in  line :
                self.ask_option()
            else:
                print line
                self.ask_option()

The Factory

    class TimeUUIdClientFactory(ClientFactory):
        def startedConnecting(self, connector):
            print 'Started to connect.'

        def buildProtocol(self, addr):
            print 'Connected.'
            return TimeUUIdClient()

        def clientConnectionLost(self, connector, reason):
            reactor.stop()

        def clientConnectionFailed(self, connector, reason):
            reactor.stop()

Running the client

    # create factory protocol and application
    factory = TimeUUIdClientFactory()

    # connect factory to this host and port
    reactor.connectTCP('localhost', 1320, factory)

    # run client
    reactor.run()

Source code can be viewed at this link.