~certify-web-dev/twisted/certify-trunk

« back to all changes in this revision

Viewing changes to doc/howto/servers.xhtml

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2004-06-21 22:01:11 UTC
  • mto: (2.2.3 sid)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20040621220111-vkf909euqnyrp3nr
Tags: upstream-1.3.0
ImportĀ upstreamĀ versionĀ 1.3.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?xml version="1.0"?>
 
2
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 
3
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
4
 
 
5
<html xmlns="http://www.w3.org/1999/xhtml">
 
6
  <head>
 
7
    <title>Writing Servers</title>
 
8
  </head>
 
9
 
 
10
  <body>
 
11
    <h1>Writing Servers</h1>
 
12
 
 
13
    <h2>Overview</h2>
 
14
 
 
15
    <p>Twisted is a framework designed to be very flexible and let
 
16
    you write powerful servers. The cost of this flexibility is a
 
17
    few layers in the way to writing your server.</p>
 
18
 
 
19
    <p>This document describes the
 
20
    <code class="API" base="twisted.internet.protocol">Protocol</code>
 
21
    layer, where you 
 
22
    implement protocol parsing and handling.  If you are implementing 
 
23
    an application then you should read this document second, after 
 
24
    first reading the top level overview of how to begin writing your 
 
25
    Twisted application, in <a href="plugin.xhtml">Writing Plug-Ins 
 
26
    for Twisted</a>. This document is only relevant to TCP, SSL and
 
27
    Unix socket servers, there is a <a href="udp.xhtml">separate document</a>
 
28
     for UDP.</p>
 
29
 
 
30
    <p>Your protocol handling class will usually subclass <code
 
31
    class="API">twisted.internet.protocol.Protocol</code>. Most
 
32
    protocol handlers inherit either from this class or from one of
 
33
    its convenience children. An instance of the protocol class
 
34
    might be instantiated per-connection, on demand, and might go
 
35
    away when the connection is finished. This means that
 
36
    persistent configuration is not saved in the
 
37
    <code>Protocol</code>.</p>
 
38
 
 
39
    <p>The persistent configuration is kept in a Factory class,
 
40
    which usually inherits from <code
 
41
    class="API">twisted.internet.protocol.Factory</code>. The
 
42
    default factory class just instantiates each <code>Protocol</code>, and then
 
43
    sets on it an attribute called <code>factory</code> which
 
44
    points to itself. This lets every <code>Protocol</code> access,
 
45
    and possibly modify, the persistent configuration.</p>
 
46
 
 
47
    <p>It is usually useful to be able to offer the same service on
 
48
    multiple ports or network addresses. This is why the <code>Factory</code>
 
49
    does not listen to connections, and in fact does not
 
50
    know anything about the network. See <code
 
51
    class="API">twisted.internet.interfaces.IReactorTCP.listenTCP</code>,
 
52
    and the other <code>IReactor*.listen*</code> APIs for more
 
53
    information.</p>
 
54
 
 
55
    <p>This document will explain each step of the way.</p>
 
56
 
 
57
    <h2>Protocols</h2>
 
58
 
 
59
    <p>As mentioned above, this, along with auxiliary classes and
 
60
    functions, is where most of the code is. A Twisted protocol
 
61
    handles data in an asynchronous manner. What this means is that
 
62
    the protocol never waits for an event, but rather responds to
 
63
    events as they arrive from the network.</p>
 
64
 
 
65
    <p>Here is a simple example:</p>
 
66
<pre class="python">
 
67
from twisted.internet.protocol import Protocol
 
68
 
 
69
class Echo(Protocol):
 
70
 
 
71
    def dataReceived(self, data):
 
72
        self.transport.write(data)
 
73
</pre>
 
74
 
 
75
    <p>This is one of the simplest protocols. It simply writes back
 
76
    whatever is written to it, and does not respond to all events. Here is an
 
77
    example of a Protocol responding to another event:</p>
 
78
<pre class="python">
 
79
from twisted.internet.protocol import Protocol
 
80
 
 
81
class QOTD(Protocol):
 
82
 
 
83
    def connectionMade(self):
 
84
        self.transport.write("An apple a day keeps the doctor away\r\n") 
 
85
        self.transport.loseConnection()
 
86
</pre>
 
87
 
 
88
    <p>This protocol responds to the initial connection with a well
 
89
    known quote, and then terminates the connection.</p>
 
90
 
 
91
    <p>The connectionMade event is usually where set up of the
 
92
    connection object happens, as well as any initial greetings (as
 
93
    in the QOTD protocol above, which is actually based on RFC
 
94
    865). The <code>connectionLost</code> event is where tearing down of any
 
95
    connection-specific objects is done. Here is an example:</p>
 
96
<pre class="python">
 
97
from twisted.internet.protocol import Protocol
 
98
 
 
99
class Echo(Protocol):
 
100
 
 
101
    def connectionMade(self):
 
102
        self.factory.numProtocols = self.factory.numProtocols+1 
 
103
        if self.factory.numProtocols &gt; 100:
 
104
            self.transport.write("Too many connections, try later") 
 
105
            self.transport.loseConnection()
 
106
 
 
107
    def connectionLost(self, reason):
 
108
        self.factory.numProtocols = self.factory.numProtocols-1
 
109
 
 
110
    def dataReceived(self, data):
 
111
        self.transport.write(data)
 
112
</pre>
 
113
 
 
114
    <p>Here <code>connectionMade</code> and
 
115
    <code>connectionLost</code> cooperate to keep a count of the
 
116
    active protocols in the factory. <code>connectionMade</code>
 
117
    immediately closes the connection if there are too many active
 
118
    protocols.</p>
 
119
 
 
120
    <h3>Using the Protocol</h3>
 
121
 
 
122
    <p>In this section, I will explain how to test your protocol
 
123
    easily. (In order to see how you should write a production-grade Twisted
 
124
    server, though, you should read the <a href="plugin.xhtml" >Writing Plug-Ins
 
125
    for Twisted</a> HOWTO as well).</p>
 
126
 
 
127
    <p>Here is code that will run the QOTD server discussed
 
128
    earlier</p>
 
129
<pre class="python">
 
130
from twisted.internet.protocol import Protocol, Factory
 
131
from twisted.internet import reactor
 
132
 
 
133
class QOTD(Protocol):
 
134
 
 
135
    def connectionMade(self):
 
136
        self.transport.write("An apple a day keeps the doctor away\r\n") 
 
137
        self.transport.loseConnection()
 
138
 
 
139
# Next lines are magic:
 
140
factory = Factory()
 
141
factory.protocol = QOTD
 
142
 
 
143
# 8007 is the port you want to run under. Choose something &gt;1024
 
144
reactor.listenTCP(8007, factory)
 
145
reactor.run()
 
146
</pre>
 
147
 
 
148
    <p>Don't worry about the last 6 magic lines -- you will
 
149
    understand what they do later in the document.</p>
 
150
 
 
151
    <h3>Helper Protocols</h3>
 
152
 
 
153
    <p>Many protocols build upon similar lower-level abstraction.
 
154
    The most popular in internet protocols is being line-based.
 
155
    Lines are usually terminated with a CR-LF combinations.</p>
 
156
 
 
157
    <p>However, quite a few protocols are mixed - they have
 
158
    line-based sections and then raw data sections. Examples
 
159
    include HTTP/1.1 and the Freenet protocol.</p>
 
160
 
 
161
    <p>For those cases, there is the <code>LineReceiver</code>
 
162
    protocol. This protocol dispatches to two different event
 
163
    handlers - <code>lineReceived</code> and
 
164
    <code>rawDataReceived</code>. By default, only
 
165
    <code>lineReceived</code> will be called, once for each line.
 
166
    However, if <code>setRawMode</code> is called, the protocol
 
167
    will call <code>rawDataReceived</code> until
 
168
    <code>setLineMode</code> is called again.</p>
 
169
 
 
170
    <p>Here is an example for a simple use of the line
 
171
    receiver:</p>
 
172
<pre class="python">
 
173
from twisted.protocols.basic import LineReceiver
 
174
 
 
175
class Answer(LineReceiver):
 
176
 
 
177
    answers = {'How are you?': 'Fine', None : "I don't know what you mean"}
 
178
 
 
179
    def lineReceived(self, line):
 
180
        if self.answers.has_key(line):
 
181
            self.sendLine(self.answers[line])
 
182
        else:
 
183
            self.sendLine(self.answers[None])
 
184
</pre>
 
185
 
 
186
    <p>Note that the delimiter is not part of the line.</p>
 
187
 
 
188
    <p>Several other, less popular, helpers exist, such as a
 
189
    netstring based protocol and a prefixed-message-length
 
190
    protocol.</p>
 
191
 
 
192
    <h3>State Machines</h3>
 
193
 
 
194
    <p>Many Twisted protocol handlers need to write a state machine
 
195
    to record the state they are at. Here are some pieces of advice
 
196
    which help to write state machines:</p>
 
197
 
 
198
    <ul>
 
199
      <li>Don't write big state machines. Prefer to write a state
 
200
      machine which deals with one level of abstraction at a
 
201
      time.</li>
 
202
 
 
203
      <li>Use Python's dynamicity to create open ended state
 
204
      machines. See, for example, the code for the SMTP
 
205
      client.</li>
 
206
 
 
207
      <li>Don't mix application-specific code with Protocol
 
208
      handling code. When the protocol handler has to make an
 
209
      application-specific call, keep it as a method call.</li>
 
210
    </ul>
 
211
 
 
212
    <h2>Factories</h2>
 
213
 
 
214
    <p>As mentioned before, usually the class <code
 
215
    class="API">twisted.internet.protocol.Factory</code> works,
 
216
    and there is no need to subclass it. However, sometimes there
 
217
    can be factory-specific configuration of the protocols, or
 
218
    other considerations. In those cases, there is a need to
 
219
    subclass <code>Factory</code>.</p>
 
220
 
 
221
    <p>For a factory which simply instantiates instances of a
 
222
    specific protocol class, simply instantiate
 
223
    <code>Factory</code>, and sets its <code>protocol</code> attribute:</p>
 
224
<pre class="python">
 
225
from twisted.internet.protocol import Factory
 
226
from twisted.protocols.wire import Echo
 
227
 
 
228
myFactory = Factory()
 
229
myFactory.protocol = Echo
 
230
</pre>
 
231
 
 
232
    <p>If there is a need to easily construct factories for a
 
233
    specific configuration, a factory function is often useful:</p>
 
234
<pre class="python">
 
235
from twisted.internet.protocol import Factory, Protocol
 
236
 
 
237
class QOTD(Protocol):
 
238
 
 
239
    def connectionMade(self):
 
240
        self.transport.write(self.factory.quote+'\r\n')
 
241
        self.transport.loseConnection()
 
242
 
 
243
 
 
244
def makeQOTDFactory(quote=None):
 
245
    factory = Factory()
 
246
    factory.protocol = QOTD
 
247
    factory.quote = quote or 'An apple a day keeps the doctor away'
 
248
    return factory
 
249
</pre>
 
250
 
 
251
    <p>A Factory has two methods to perform application-specific
 
252
    building up and tearing down (since a Factory is frequently
 
253
    persisted, it is often not appropriate to do them in <code>__init__</code>
 
254
    or <code>__del__</code>, and would frequently be too early or too late).</p>
 
255
 
 
256
    <p>Here is an example of a factory which allows its Protocols
 
257
    to write to a special log-file:</p>
 
258
<pre class="python">
 
259
from twisted.internet.protocol import Factory
 
260
from twisted.protocols.basic import LineReceiver
 
261
 
 
262
 
 
263
class LoggingProtocol(LineReceiver):
 
264
 
 
265
    def lineReceived(self, line):
 
266
        self.factory.fp.write(line+'\n')
 
267
 
 
268
 
 
269
class LogfileFactory(Factory):
 
270
 
 
271
    protocol = LoggingProtocol
 
272
 
 
273
    def __init__(self, fileName):
 
274
        self.file = fileName
 
275
 
 
276
    def startFactory(self):
 
277
        self.fp = open(self.file, 'a')
 
278
 
 
279
    def stopFactory(self):
 
280
        self.fp.close()
 
281
</pre>
 
282
 
 
283
    <h3>Putting it All Together</h3>
 
284
 
 
285
    <p>So, you know what factories are, and want to run the QOTD
 
286
    with configurable quote server, do you? No problems, here is an
 
287
    example.</p>
 
288
 
 
289
<pre class="python">
 
290
from twisted.internet.protocol import Factory, Protocol
 
291
from twisted.internet import reactor
 
292
 
 
293
class QOTD(Protocol):
 
294
 
 
295
    def connectionMade(self):
 
296
        self.transport.write(self.factory.quote+'\r\n')
 
297
        self.transport.loseConnection()
 
298
 
 
299
 
 
300
class QOTDFactory(Factory):
 
301
 
 
302
    protocol = QOTD
 
303
 
 
304
    def __init__(self, quote=None):
 
305
        self.quote = quote or 'An apple a day keeps the doctor away'
 
306
 
 
307
reactor.listenTCP(8007, QOTDFactory("configurable quote"))
 
308
reactor.run()
 
309
</pre>
 
310
 
 
311
    <p>The only lines you might not understand are the last two.</p>
 
312
 
 
313
<p><code class="API"
 
314
base="twisted.internet.interfaces.IReactorTCP">listenTCP</code> is
 
315
the method which connects a <code>Factory</code> to the network.
 
316
It uses the reactor interface, which lets many different loops handle
 
317
the networking code, without modifying end-user code, like this.
 
318
As mentioned above, if you want to write your code to be a production-grade
 
319
Twisted server, and not a mere 20-line hack, you will want to
 
320
use <a href="application.xhtml">the Application object</a>.</p>
 
321
 
 
322
  </body>
 
323
</html>