使用Python的Twisted框架实现一个简单的服务器


Posted in Python onApril 16, 2015

预览
  twisted是一个被设计的非常灵活框架以至于能够让你写出非常强大的服务器。这种灵活的代价是需要好通过好几个层次来实现你的服务器, 本文档描述的是Protocol层,你将在这个层次中执行协议的分析和处理,如果你正在执行一个应用程序,那么你应该在读过top level的为twisted写插件一节中的怎样开始写twisted应用程序之后阅读本章。这个文档只是和TCP,SSL和Unix套接字服务器有关,同时也将有另一份文档专门讲解UDP。
  你的协议处理类通常是twisted.internet.protocol.Protocol的子类。许多协议处理继承于该类或者比该类更加方便的该类的子类。一个protocol类的实例可能反复连接,也可能在连接关闭之后销毁。这就意味着这些持续不断的配置信息不是保存在Protocol中。
   这些持久性的配置被保存在工厂(Factory)类中,这些工厂类通常继承至twisted.internet.protocol.Factory,默认 的工厂类仅仅是实例化每个Protocol,然后设置他们的factory属性为这个默认的工厂实例本身。这就让每个Protocol都被存储,然后可能 修改,于是这样就形成了Protocol的持久性。
   通常为多个端口或网络地址提供相同的服务是非常有用的。这就是为什么Factory不监听连接,并且实际上它不知道关于网络的任何事情。看 twisted.internet.interfaces.IReactorTCP.listenTCP,另一个IReactor*.listen*获得 更多的信息。

   本文档将要讲解各个步骤。

Protocol
    如上所述,这里将通过更多代码的辅助类和函数来了解它。一个twisted protocl通过异步方式处理数据。这就意味着protocol从不等待任何事件。相反的是在事件通过网络到达的时候作出响应。

from twisted.internet.protocol import Protocol
class Echo(Protocol):
 def dataReceived(self,data):
  self.transport.writed(data)

这是个非常简单的协议处理,仅仅是在获得数据的事件中简单的将接收到的数据发送回去,并没有对所有的事件进行响应。这里有一个Protocol响应其他事件的例子如下:

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()

    本Protocl在一个已知的引用刚开始连接上来的时候作出响应,发送了一条消息,然后终止了连接connectionMade事件通常是在由于连接对象建立初始连接时触发,就像上面的QOTD类实际上是RFC865号文档的一个协议基类connectionLost事件将在断开连接的时候触发。实例:  
       

<span style="font-family: Monospaced; color: #0000a0;"><strong>PythonCode: </strong></span><table style="width: 100%; height: 20px;" align="center" bgcolor="#e3dfe3" border="1" bordercolor="#9da7ac" cellpadding="0" cellspacing="0"> 
  <tbody><tr><td> 
     <div class="textBackGround" style="font-family:Courier New;font-size:9pt;"><pre><span style="color: blue;">from</span> twisted.internet.protocol <span style="color: blue;">import</span> Protocol 
 <span style="color: blue;">class</span> Echo(Protocol): 
  <span style="color: blue;">def</span> connectionMade(self): 
   self.factory.numProtocols = self.factory.numProtocols+1 
   <span style="color: blue;">if</span> self.factory.numProtocols > 100: 
    self.transport.write(<span style="color: #ff44a2;">"Too many connections, <span style="color: blue;">try</span> later"</span>) 
    self.transport.loseConnection() 
  <span style="color: blue;">def</span> connectionLost(self, reason): 
   self.factory.numProtocols = self.factory.numProtocols-1 
  <span style="color: blue;">def</span> dataReceived(self, data): 
   self.transport.write(data)</pre> 
 </div> 
 </td> 
 </tr> 
 </tbody> 
 </table>

    本实例中,connectionMade和connectionLost相互协作工作以保持factory内部的活动连接数量最多为100。每当有用户协议连接近来的时候,就先检测factory内部的活动连接数,如果数量超过100,就发送连接数太多等下试的消息,然后断开连接而connectionLost则在断开一个协议的时候触发,减去factory内部的协议数量。

Using the Protocol

          在本节,我将要讲解怎样简单的去测试你的protocol。(想知道如何写出一个好的twisted的服务器,请看 <a href="http://fantix.org/twisted-doc-zh/nightly/online/howto/plugin.html">Writing Plug-Ins<br>    for Twisted</a>),这里有一个代码将运行我们上面谈论的QOTD服务器:  
   

<!-- 
 .textBackGround {background-color: #F0F5FD;} 
 --> 
  <span style="font-family: Monospaced; color: #0000a0;"><strong>PythonCode: </strong></span><table style="width: 100%; height: 20px;" align="center" bgcolor="#e3dfe3" border="1" bordercolor="#9da7ac" cellpadding="0" cellspacing="0"> 
  <tbody><tr><td> 
     <div class="textBackGround" style="font-family:Courier New;font-size:9pt;"><pre><span style="color: blue;">from</span> twisted.internet.protocol <span style="color: blue;">import</span> Protocol, Factory 
 <span style="color: blue;">from</span> twisted.internet <span style="color: blue;">import</span> reactor 
 <span style="color: blue;">class</span> QOTD(Protocol): 
  <span style="color: blue;">def</span> connectionMade(self): 
   self.transport.write(<span style="color: #ff44a2;">"An apple a day keeps the doctor away/r/n"</span>) 
   self.transport.loseConnection() 
  
 <span style="color: green;"># Next lines are magic:</span> 
 factory = Factory() 
 factory.protocol = QOTD 
  
 <span style="color: green;"># 8007 <span style="color: blue;">is</span> the port you want to run under. Choose something >1024</span> 
 reactor.listenTCP(8007, factory) 
 reactor.run()</pre> 
 </div> 
 </td> 
 </tr> 
 </tbody> 
 </table>

    不必担心最后面的6条代码,稍后你将会在本文档中了解到他们。<br> 

Helper Protocols

     大部分protocols依赖于同类别的更低层次的超级类。最受欢迎的互联网协议是基于行,行通常是由CR_LF(回车换行组成)
然而,也有相当一部分协议是混合的,他们具有线性的基本节点,也有原始数据节点,比如HTTP/1.1。
     在这样的情况下,我们可以使用LineReceiver,本协议类有两个不同的事件处理方法,lineReceived和rawDataReceived
默认情况下,只有lineReceived会被调用,每次读取一行,然而如果setRawMode被调用,protocol将调用rawDataReceived
来处理直到setLineMode被调用。下面有一个简单的例子说明如何使用lineReceiver:

    PythonCode:             

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])

注意:界定符不是命令行的一部分
其他也有一些不流行的协议依然存在,比如netstring based 和 a prefixed-message-length

State Machines

     许多twisted protocol handlers需要编写一个状态机来记录他们当前的状态,这里有几点编写状态机的建议:
   1、不要编写大状态机,宁愿去实现一个抽象的状态机类
   2、使用python的动态性质去创建没有限制的状态机,比如SMTP客户端
   3、不要混合特定应用程序代码和协议处理代码,当协议处理器已经提出一个特别的具体要求,保持它作为一个方法调用。

Factories(工厂类)

     如前面所说,通常twisted.internet.protocol.Factory不必子类化就可以开始工作。然而有时候protocol需要具体的
特殊的工厂配置信息或其他需求,在这样的情况下,就需要进行子类化了。
    对于Factory来说,他只是简单的实例化特殊的 protocol协议类,实例化Factory,并且设置protocol属性:

    PythonCode:             

from twisted.internet.protocol import Factory
from twisted.protocols.wire import Echo

myFactory = Factory()
myFactory.protocol = Echo

如果需要简单的去构造一个有具体特殊信息的工厂类,那么一个factory函数是非常有用的:

PythonCode:

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

一个Factory有两个方法以执行特定于应用程序的建立和拆除(由于一个Factory通常存在,所以常规下一般不在__init__或者
__del__中给他们分配与回收,有可能太早或太晚)。
下面是一个Factory的例子,本例将允许Protocol写一个日志文件:   
PythonCode:

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()

Putting it All Together(综合)

    现在你已经了解了Factory并且想要执行QOTD作为一个可配置的quote服务器是吗?没有问题这里就有一个代码:
    PythonCode:             

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()

就是最后两句代码,还需要去理解。
listenTCP是一个将Factory连接到网络的方法,他使用了reactor的接口,让许多不同的循环处理网络代码,而不需要修改的
最终用户代码,就像这样。如前面所说,如果你想要写一个好的twisted服务器,而不是仅仅的20行,那么你需要使用 the Application object.

Python 相关文章推荐
Python 调用VC++的动态链接库(DLL)
Sep 06 Python
Python日期操作学习笔记
Oct 07 Python
python使用cookielib库示例分享
Mar 03 Python
跟老齐学Python之dict()的操作方法
Sep 24 Python
使用wxPython获取系统剪贴板中的数据的教程
May 06 Python
Python中数字以及算数运算符的相关使用
Oct 12 Python
python 读写txt文件 json文件的实现方法
Oct 22 Python
浅析python实现scrapy定时执行爬虫
Mar 04 Python
Python3简单实现串口通信的方法
Jun 12 Python
Django框架创建项目的方法入门教程
Nov 04 Python
tensorflow 获取所有variable或tensor的name示例
Jan 04 Python
解决TensorFlow训练内存不断增长,进程被杀死问题
Feb 05 Python
使用Python的Twisted框架编写简单的网络客户端
Apr 16 #Python
从Python的源码浅要剖析Python的内存管理
Apr 16 #Python
用Python实现换行符转换的脚本的教程
Apr 16 #Python
Python下的subprocess模块的入门指引
Apr 16 #Python
Python下的twisted框架入门指引
Apr 15 #Python
Python代码调试的几种方法总结
Apr 15 #Python
详解Python中with语句的用法
Apr 15 #Python
You might like
php 信息采集程序代码
2009/03/17 PHP
PHP memcache扩展的三种安装方法
2009/04/26 PHP
php中获取指定IP的物理地址的代码(正则表达式)
2011/06/23 PHP
Win下如何安装PHP的APC拓展
2013/08/07 PHP
php中的字符编码转换函数用法示例
2014/10/20 PHP
快速解决PHP调用Word组件DCOM权限的问题
2017/12/27 PHP
PHP设计模式之原型设计模式原理与用法分析
2018/04/25 PHP
IE6,IE7下js动态加载图片不显示错误
2010/07/17 Javascript
Javascript公共脚本库系列(一): 弹出层脚本
2011/02/24 Javascript
JavaScript 放大镜 放大倍率和视窗尺寸
2011/05/09 Javascript
MooBox 基于Mootools的对话框插件
2012/01/20 Javascript
JavaScript高级程序设计(第3版)学习笔记10 再访js对象
2012/10/11 Javascript
node.js中的fs.utimesSync方法使用说明
2014/12/15 Javascript
jQuery简单获取键盘事件的方法
2016/01/22 Javascript
Vue.js render方法使用详解
2017/04/05 Javascript
Jquery中attr与prop的区别详解
2017/05/27 jQuery
详解tween.js的使用教程
2017/09/14 Javascript
bootstrap treeview 扩展addNode方法动态添加子节点的方法
2017/11/21 Javascript
在vue中使用css modules替代scroped的方法
2018/03/10 Javascript
React 路由懒加载的几种实现方案
2018/10/23 Javascript
JavaScript交换变量的常用方法小结【4种方法】
2020/05/07 Javascript
jQuery实现的分页插件完整示例
2020/05/26 jQuery
Python标准异常和异常处理详解
2015/02/02 Python
python递归函数绘制分形树的方法
2018/06/22 Python
python自动生成model文件过程详解
2019/11/02 Python
Python如何实现在字符串里嵌入双引号或者单引号
2020/03/02 Python
PyQt5+Pycharm安装和配置图文教程详解
2020/03/24 Python
Python 私有属性和私有方法应用场景分析
2020/06/19 Python
Pycharm 2020.1 版配置优化的详细教程
2020/08/07 Python
通过CSS3的object-fit来调整图片适配尺寸的技巧简介
2016/02/27 HTML / CSS
HTML5 语音搜索(淘宝店语音搜素)
2013/01/03 HTML / CSS
法国在线药房:Shop Pharmacie
2019/11/26 全球购物
Can a struct inherit from another class? (结构体能继承类吗)
2014/07/22 面试题
2015年度党员个人总结
2015/02/14 职场文书
政审证明范文
2015/06/19 职场文书
2016年119消防宣传日活动总结
2016/04/05 职场文书