编写TCP服务器(Writing a TCP Server) -- dreamingk [2004-08-09 01:56:45]

概要(Overview)

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.

Twisted被设计为一个非常灵活的框架,可以使用它编写强大的服务器.这种灵活性的代价是当你编写服务器的时候需要分好几层.

This document describes the Protocol 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 Writing Plug-Ins for Twisted. This document is only relevant to TCP, SSL and Unix socket servers, there is a separate document for UDP.

本文档描述协议层,在这里定义协议的解析和处理.如果你正在编写一个应用的话这应该是你读的第二份文档,第一份是关于如何开始编写你的Twsited服务器,参见"编写Twised插件".本文档关注TCP,SSL和Unix socket服务器,关于UDP有另一份文档.

Your protocol handling class will usually subclass twisted.internet.protocol.Protocol. 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 Protocol.

通常你的协议处理类应该是twisted.internet.protocol.Protocol的子类.大部分协议处理函数或者继承自这个类,或者继承自它的一个更适合的子类.依据需求,协议类的实例可能为每个连接实例化,可能在连接结束的时候被析构.这就意味着不要在协议类中包含需要持久配置.

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

持久配置应该包含在工厂类中,工厂类通常继承自twisted.internet.protocol.Factory.缺省的工厂类只是实例化协议类,并且设置协议类的属性factory指向自己.这样就可以让每个协议类可以访问甚至修改持久配置.

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

在不同的网络地址和端口提供同样的服务通常是很有用的.所以工厂并不去监听连接,实事上工厂对于网络一无所知.请阅读twisted.internet.interfaces.IReactorTCP.listenTCP和IReactor*.listen*得到更多的信息.

This document will explain each step of the way.

本文档将解释上述的每个步骤.

协议(Protocols)

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.

就像上面提到的,在大多数代码中,协议和其它辅助类和函数协同工作.Twisted协议以异步方式处理数据.这意味着协议从来不会去等待一个事件,而是当事件从网络到来的时候响应它.

Here is a simple example:

这里有个简单的例子

   1     from twisted.internet.protocol import Protocol
   2 
   3     class Echo(Protocol):
   4 
   5         def dataReceived(self,data):
   6             self.transport.write(data)

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:

这是一个最简单的协议,它只是把收到的内容写回去,并没有响应所有的事件.下一个例子演示响应其它事件

   1     from twisted.internet.protocol import Protocol
   2 
   3     class QOTD(Protocol):
   4 
   5         def connectionMade(self):
   6             self.transport.write("An apple a day keeps the doctor away\r\n")
   7             self.transport.loseConnection()

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

这个协议在连接建立后发送一条格言,然后断开连接.

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 connectionLost event is where tearing down of any connection-specific objects is done. Here is an example:

connectionMake事件通常在建立连接的时候触发,发送一条欢迎信息(就像上面的QOTD协议,该协议基于RFC865).conectionLost事件在连接断开后触发.看这个例子

   1     from twisted.internet.protocol import Protocol
   2 
   3     class Echo(Protocol):
   4 
   5         def connectionMake(self):
   6            self.factory.numProtocols = self.factory.numProtocols + 1
   7            self.factory.numProtocols > 100:
   8                self.transport.write("Too many connections, try later")
   9                self.transport.loseConnection()
  10 
  11         def connectionLost(self,reason):
  12             self.factory.numProtocols = self.factory.numProtocols - 1
  13 
  14         def dataReceived(self,data):
  15             self.transport.write(data)

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

这里connnectionMade事件和connectionLost事件协同维护工厂中的活动计数.如果活动计数太高,connectionMake就立即断开连接.

使用协议(Using the Protocol)

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 Writing Plug-Ins for Twisted HOWTO as well).

在这一节中,我将解释如何使你的协议便于测试.(尽管如果你想写出产品级的Twisted服务器,"编写Twisted插件"才是你最应该读的).

Here is code that will run the QOTD server discussed earlier

下面的代码运行前面讨论过的QOTD服务器

   1     from twisted.internet.protocol import Protocol, Factory
   2     from twisted.internet import reactor
   3 
   4     class QOTD(Protocol):
   5     
   6         def connectionMake(self):
   7             self.transport.write("An apple a day keeps the doctor away\r\n")
   8             self.transport.loseConnection()
   9     
  10     # 魔术发生在下面一行:
  11     factory = Factory()
  12     factory.protocol = QOTD
  13     
  14     # 8007是你想服务运行的端口,其实大于1024都可以 
  15     reactor.listenTCP(8007,factory)
  16     reactor.run()

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

别为不明白具有魔力的最后六行担心--在文档的后面部分你会明白他们在干什么的.

助手协议(Helper Protocols)

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.

很多协议的底层数据提取都是一样的.大部分流行的因特网协议都是基于行的.行通常由"回车换行"这个组合来结束

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.

也有非常少的协议是混合的--他们有基于行的部分也有原始数据部分.这样的例子包括HTTP/1.1和Freenet协议

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

LineReceiver协议适用于这些情况.该协议分发两种不同的事件处理器 - lineReceived和rawDataReceived.缺省情况下每行只有lineReceived会被调用.然而,如果setRawMode被调用后,协议就会触发rawDataReceived,直到setLineMode被调用.

Here is an example for a simple use of the line receiver:

这里有个行接受的简单示例:

   1     from twisted.protocols.basic import LineReceiver
   2 
   3     class Answer(LineReceiver):
   4         answers = {'How are you?': 'Fine', None: "I don't know what you mean"}
   5     
   6         def lineReceived(self, line):
   7             if self.answers.has_key(line):
   8                 self.sendLine(self.answers[line])
   9             else:
  10                 self.sendLine(self.answers[None])

Note that the delimiter is not part of the line.

注意分隔符('\r\n')不是行的一部分

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

还有一些不是特别流行的助手协议,比方说netstring based协议和prefixed-message-length协议

状态机(State Machines)

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:

很多Twisted协议处理器需要实现状态机来纪录它目前所处的状态.这里有一些关于状态机的建议:

工厂(Factories)

As mentioned before, usually the class twisted.internet.protocol.Factory 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 Factory.

如同前面提到的,通常twisted.internet.protocol.Factory就可以工作了,而不需要派生一个子类.然而,有时协议需要一些工厂维护的持续信息或者其它情况,我们就需要一个派生自Factory的子类

For a factory which simply instantiates instances of a specific protocol class, simply instantiate Factory, and sets its protocol attribute:

对于一个工厂来说,就是实例化协议类,实例化工厂,设置工厂的protocol属性

   1     from twisted.internet.protocol import Factory
   2     from twisted.protocols.wire import Echo
   3     myFactory = Factory()
   4     myFactory.protocol = Echo

If there is a need to easily construct factories for a specific configuration, a factory function is often useful:

如果需要方便的为特殊配置构造工厂,一个工厂函数很有用:

   1     from twisted.internet.protocol import Factory, Protocl
   2 
   3     class QOTD(Protocol):
   4         
   5         def connectionMake(self):
   6             self.transport.write(elf.factory.quote+'\r\n')
   7             self.transport.loseConnection()
   8 
   9     def makeQOTDFactory(quote=None):
  10         factory = Factory()
  11         factory.protocol = QOTD
  12         factory.quote = quote or 'An apple a day keeps the doctor away'
  13         return factory

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 init or del, and would frequently be too early or too late).

工厂有两个执行特殊应用的方法building up和tearing down(由于工程是持续的,调用init或者del都是不合适的)

<<<这两句没太看明白>>>

Here is an example of a factory which allows its Protocols to write to a special log-file:

下面这个例子演示了允许协议写一个Log文件.

   1     from twisted.internet.protocol import Factory
   2     from twisted.protocols.basic import LineReceiver
   3 
   4     class LoggingProtocol(LineReceiver):
   5 
   6         def lineReceived(self, line):
   7             self.factory.fp.write(line + '\n')
   8 
   9     class LogfileFactory(Factory):
  10         protocol = LoggingProtocol
  11 
  12         def __init__(self, fileName):
  13              self.file = fileName
  14 
  15         def startFactory(self):
  16              self.fp = open(self.file, 'a')
  17 
  18         def stopFactory(self):
  19              self.fp.close()

组织在一起(Putting it All Together)

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

现在,你知道了什么是工厂,下来想要运行QOTD为一个可配置的引用服务器,是吧?没问题,示例如下:

   1     from twisted.internet.protocol import Factory, Protocl
   2     from twisted.internet import reactor
   3 
   4     class QOTD(Protocol):
   5         def connectionMade(self):
   6             self.transport.write(self.factory.quote + '\r\n')
   7             self.transport.loseConnection()
   8 
   9     class QOTDFactory(Factory):
  10         protocol = QOTD
  11         
  12         def __init__(self, quote=None):
  13             self.quote = quote or 'An apple a day keeps the doctor away'
  14 
  15     reactor.listenTCP(8007, QOTDFactory("configurable quote"))
  16     reactor.run()

The only lines you might not understand are the last two.

只有最后两行你还可能不理解

listenTCP is the method which connects a Factory 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 the Application object.

listenTCP是一个连接到网络的方法.它使用reactor接口,reactor接口使我们不用修改代码就可以完成不同的处理循环.就像前面提及的,如果你像写一个产品级的Twisted服务器而不是只有20行代码,你可能想使用"应用程序对象"

version 1.3.0

PyTwisted/LowLevelNetworkingEventLoop/LLNEL3 (last edited 2010-03-10 07:17:03 by ZoomQuiet)