servers.xhtml   [plain text]


<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Writing Servers</title>
  </head>

  <body>
    <h1>Writing Servers</h1>

    <h2>Overview</h2>

    <p>Twisted is a framework designed to be very flexible and let
    you write powerful servers. The cost of this flexibility is a
    few layers in the way to writing your server.</p>

    <p>This document describes the
    <code class="API" base="twisted.internet.protocol">Protocol</code>
    layer, where you 
    implement protocol parsing and handling.  If you are implementing 
    an application then you should read this document second, after 
    first reading the top level overview of how to begin writing your 
    Twisted application, in <a href="plugin.xhtml">Writing Plug-Ins 
    for Twisted</a>. This document is only relevant to TCP, SSL and
    Unix socket servers, there is a <a href="udp.xhtml">separate document</a>
     for UDP.</p>

    <p>Your protocol handling class will usually subclass <code
    class="API">twisted.internet.protocol.Protocol</code>. Most
    protocol handlers inherit either from this class or from one of
    its convenience children. An instance of the protocol class
    might be instantiated per-connection, on demand, and might go
    away when the connection is finished. This means that
    persistent configuration is not saved in the
    <code>Protocol</code>.</p>

    <p>The persistent configuration is kept in a Factory class,
    which usually inherits from <code
    class="API">twisted.internet.protocol.Factory</code>. The
    default factory class just instantiates each <code>Protocol</code>, and then
    sets on it an attribute called <code>factory</code> which
    points to itself. This lets every <code>Protocol</code> access,
    and possibly modify, the persistent configuration.</p>

    <p>It is usually useful to be able to offer the same service on
    multiple ports or network addresses. This is why the <code>Factory</code>
    does not listen to connections, and in fact does not
    know anything about the network. See <code
    class="API">twisted.internet.interfaces.IReactorTCP.listenTCP</code>,
    and the other <code>IReactor*.listen*</code> APIs for more
    information.</p>

    <p>This document will explain each step of the way.</p>

    <h2>Protocols</h2>

    <p>As mentioned above, this, along with auxiliary classes and
    functions, is where most of the code is. A Twisted protocol
    handles data in an asynchronous manner. What this means is that
    the protocol never waits for an event, but rather responds to
    events as they arrive from the network.</p>

    <p>Here is a simple example:</p>
<pre class="python">
from twisted.internet.protocol import Protocol

class Echo(Protocol):

    def dataReceived(self, data):
        self.transport.write(data)
</pre>

    <p>This is one of the simplest protocols. It simply writes back
    whatever is written to it, and does not respond to all events. Here is an
    example of a Protocol responding to another event:</p>
<pre class="python">
from twisted.internet.protocol import Protocol

class QOTD(Protocol):

    def connectionMade(self):
        self.transport.write("An apple a day keeps the doctor away\r\n") 
        self.transport.loseConnection()
</pre>

    <p>This protocol responds to the initial connection with a well
    known quote, and then terminates the connection.</p>

    <p>The connectionMade event is usually where set up of the
    connection object happens, as well as any initial greetings (as
    in the QOTD protocol above, which is actually based on RFC
    865). The <code>connectionLost</code> event is where tearing down of any
    connection-specific objects is done. Here is an example:</p>
<pre class="python">
from twisted.internet.protocol import Protocol

class Echo(Protocol):

    def connectionMade(self):
        self.factory.numProtocols = self.factory.numProtocols+1 
        if self.factory.numProtocols &gt; 100:
            self.transport.write("Too many connections, try later") 
            self.transport.loseConnection()

    def connectionLost(self, reason):
        self.factory.numProtocols = self.factory.numProtocols-1

    def dataReceived(self, data):
        self.transport.write(data)
</pre>

    <p>Here <code>connectionMade</code> and
    <code>connectionLost</code> cooperate to keep a count of the
    active protocols in the factory. <code>connectionMade</code>
    immediately closes the connection if there are too many active
    protocols.</p>

    <h3>Using the Protocol</h3>

    <p>In this section, I will explain how to test your protocol
    easily. (In order to see how you should write a production-grade Twisted
    server, though, you should read the <a href="plugin.xhtml" >Writing Plug-Ins
    for Twisted</a> HOWTO as well).</p>

    <p>Here is code that will run the QOTD server discussed
    earlier</p>
<pre class="python">
from twisted.internet.protocol import Protocol, Factory
from twisted.internet import reactor

class QOTD(Protocol):

    def connectionMade(self):
        self.transport.write("An apple a day keeps the doctor away\r\n") 
        self.transport.loseConnection()

# Next lines are magic:
factory = Factory()
factory.protocol = QOTD

# 8007 is the port you want to run under. Choose something &gt;1024
reactor.listenTCP(8007, factory)
reactor.run()
</pre>

    <p>Don't worry about the last 6 magic lines -- you will
    understand what they do later in the document.</p>

    <h3>Helper Protocols</h3>

    <p>Many protocols build upon similar lower-level abstraction.
    The most popular in internet protocols is being line-based.
    Lines are usually terminated with a CR-LF combinations.</p>

    <p>However, quite a few protocols are mixed - they have
    line-based sections and then raw data sections. Examples
    include HTTP/1.1 and the Freenet protocol.</p>

    <p>For those cases, there is the <code>LineReceiver</code>
    protocol. This protocol dispatches to two different event
    handlers - <code>lineReceived</code> and
    <code>rawDataReceived</code>. By default, only
    <code>lineReceived</code> will be called, once for each line.
    However, if <code>setRawMode</code> is called, the protocol
    will call <code>rawDataReceived</code> until
    <code>setLineMode</code> is called again.</p>

    <p>Here is an example for a simple use of the line
    receiver:</p>
<pre class="python">
from twisted.protocols.basic import LineReceiver

class Answer(LineReceiver):

    answers = {'How are you?': 'Fine', None : "I don't know what you mean"}

    def lineReceived(self, line):
        if self.answers.has_key(line):
            self.sendLine(self.answers[line])
        else:
            self.sendLine(self.answers[None])
</pre>

    <p>Note that the delimiter is not part of the line.</p>

    <p>Several other, less popular, helpers exist, such as a
    netstring based protocol and a prefixed-message-length
    protocol.</p>

    <h3>State Machines</h3>

    <p>Many Twisted protocol handlers need to write a state machine
    to record the state they are at. Here are some pieces of advice
    which help to write state machines:</p>

    <ul>
      <li>Don't write big state machines. Prefer to write a state
      machine which deals with one level of abstraction at a
      time.</li>

      <li>Use Python's dynamicity to create open ended state
      machines. See, for example, the code for the SMTP
      client.</li>

      <li>Don't mix application-specific code with Protocol
      handling code. When the protocol handler has to make an
      application-specific call, keep it as a method call.</li>
    </ul>

    <h2>Factories</h2>

    <p>As mentioned before, usually the class <code
    class="API">twisted.internet.protocol.Factory</code> works,
    and there is no need to subclass it. However, sometimes there
    can be factory-specific configuration of the protocols, or
    other considerations. In those cases, there is a need to
    subclass <code>Factory</code>.</p>

    <p>For a factory which simply instantiates instances of a
    specific protocol class, simply instantiate
    <code>Factory</code>, and sets its <code>protocol</code> attribute:</p>
<pre class="python">
from twisted.internet.protocol import Factory
from twisted.protocols.wire import Echo

myFactory = Factory()
myFactory.protocol = Echo
</pre>

    <p>If there is a need to easily construct factories for a
    specific configuration, a factory function is often useful:</p>
<pre class="python">
from twisted.internet.protocol import Factory, Protocol

class QOTD(Protocol):

    def connectionMade(self):
        self.transport.write(self.factory.quote+'\r\n')
        self.transport.loseConnection()


def makeQOTDFactory(quote=None):
    factory = Factory()
    factory.protocol = QOTD
    factory.quote = quote or 'An apple a day keeps the doctor away'
    return factory
</pre>

    <p>A Factory has two methods to perform application-specific
    building up and tearing down (since a Factory is frequently
    persisted, it is often not appropriate to do them in <code>__init__</code>
    or <code>__del__</code>, and would frequently be too early or too late).</p>

    <p>Here is an example of a factory which allows its Protocols
    to write to a special log-file:</p>
<pre class="python">
from twisted.internet.protocol import Factory
from twisted.protocols.basic import LineReceiver


class LoggingProtocol(LineReceiver):

    def lineReceived(self, line):
        self.factory.fp.write(line+'\n')


class LogfileFactory(Factory):

    protocol = LoggingProtocol

    def __init__(self, fileName):
        self.file = fileName

    def startFactory(self):
        self.fp = open(self.file, 'a')

    def stopFactory(self):
        self.fp.close()
</pre>

    <h3>Putting it All Together</h3>

    <p>So, you know what factories are, and want to run the QOTD
    with configurable quote server, do you? No problems, here is an
    example.</p>

<pre class="python">
from twisted.internet.protocol import Factory, Protocol
from twisted.internet import reactor

class QOTD(Protocol):

    def connectionMade(self):
        self.transport.write(self.factory.quote+'\r\n')
        self.transport.loseConnection()


class QOTDFactory(Factory):

    protocol = QOTD

    def __init__(self, quote=None):
        self.quote = quote or 'An apple a day keeps the doctor away'

reactor.listenTCP(8007, QOTDFactory("configurable quote"))
reactor.run()
</pre>

    <p>The only lines you might not understand are the last two.</p>

<p><code class="API"
base="twisted.internet.interfaces.IReactorTCP">listenTCP</code> is
the method which connects a <code>Factory</code> to the network.
It uses the reactor interface, which lets many different loops handle
the networking code, without modifying end-user code, like this.
As mentioned above, if you want to write your code to be a production-grade
Twisted server, and not a mere 20-line hack, you will want to
use <a href="application.xhtml">the Application object</a>.</p>

  </body>
</html>