Twisted

本文章通过实际例子和用Twisted实现一个类似QQ那样的聊天室来逐步学习Twisted

一、稍微来点基础

首先,我们要明确一个概念,Twisted是一个完全事件驱动的网络框架.它允许你使用和开发完全异步的网络应用程序和协议.

Twisted常用函数:
connectionMade():函数:这个函数在客户端成功连接的时候被调用,即仅仅在连接成功的时候会调用一次
dataReceived():函数:这个函数在客户端通过网络发送数据过来的时候被调用.reactor把数据当成参数传到这个函数中,这样我们就不用自己去解析数据了
self.transport:一个Twisted的transport代表一个可以收发字节的单条连接.例如ChatProtocol的服务器它的此属性为<ChatProtocol #0 on 8123>
self.transport.loseConnection():断掉与服务器之间的连接
self.transport.getPeer:获得主机(服务器)信息例如:IPv4Address(TCP, '127.0.0.1', 35836)依次是 协议,IP地址,服务器端口号
self.transport.getPeer().host:获得主机信息的IP地址
self.transport.client属性:是一个元组,其中包含了客户端的地址(IP,端口)例如:('127.0.0.1',35753)
protocol.Factory():它被称为工厂,每次连接进来的时候,它都会"生产"一个我们的protocol对象.然后在reactor中安装一个TCP监听器以等待服务请求.当有请求进来时,创建一个DemoProtocol实例来服务那个客户端.
具体的一个小例子如下:

# coding=utf-8
from twisted.internet.protocol import Protocol, Factory
from twisted.internet import reactor

PORT = 21567

class EchoProtocol(Protocol):
    def connectionMade(self):
        print 'Got connection from', self.transport.client
    def connectionLost(self, reason):
        print self.transport.client, 'disconnected'
    def dataReceived(self, data):
        print self.transport.write(data)

factory = Factory()# 实例化Factory
factory.protocol = EchoProtocol  # 设置factory的protocol属性以便它知道使用哪个protocol与客户端通信(这就是所谓的你的自定义protocol)

reactor.listenTCP(PORT, factory)
reactor.run()

对于客户端部分,需要注意的是:

class EchoClientFactory(protocol.clientFactory):
    protocol = EchoClientProtocol
    clientConnectionLost = clientConnectionFaild= \ 
       lambda self,connector,reason:reactor.stop()
reactor.connectTCP(HOST,PORT,EchoClientFactory())
reactor.run()

脚本的最后一部分是创建一个客户端工厂,连接到服务器,然后运行reactor.注意,我们在这里实例化了客户端工厂,而不是像在服务器里那样把它传到reactor中.这是因为,我们不是等待客户端连接的服务器,服务器在有连接时要为每个连接创建一个新的protocol对象.我们只是一个客户端,所以我们只要创建一个protocol对象,连接到服务器,服务器的工厂会创建一个protocol对象来与我们对话

二、提升效率的 defferred

Twisted 官方称,“Twisted is event-based, asynchronous framework ”。
这个“异步”功能的代表就是 defferred。 defferred 的作用类似于“多线程”,负责保障多头连接、多项任务的异步执行。
当然,defferred “异步”功能的实现,与多线程完全不同,具有以下特点:

  1. defferred 产生的 event,是函数调用返回的对象;
  2. defferred 代表一个连接任务,负责报告任务执行的延迟情况和最终结果;
  3. 对defferred 的操作,通过预定的“事件响应器”(event handler)进行。 有了defferred,即可对任务的执行进行管理控制。防止程序的运行,由于等待某项任务的完成而陷入阻塞停滞,提高整体运行的效率。

请看下面的例子: 建议只关注黑体字的语句,它们反映了defferred的用法。
涉及的两个class,是Twisted建立网络连接的固定套路,后面会专门说它。
# connectiontest.py
from twisted.internet import reactor, defer,protocol

class CallbackAndDisconnectProtocol(protocol.Protocol):
# Twisted建立网络连接的固定套路
    def connectionMade(self): 
        self.factory.deferred.callback("Connected!")# “事件响应器”handleSuccess对此事件作出处理
        self.transport.loseConnection( )

class ConnectionTestFactory(protocol.ClientFactory): 
# Twisted建立网络连接的固定套路 
    protocol = CallbackAndDisconnectProtocol

    def __init__(self): 
        self.deferred = defer.Deferred( )# 报告发生了延迟事件,防止程序阻塞在这个任务上

    def clientConnectionFailed(self, connector, reason):
         self.deferred.errback(reason) # “事件响应器”handleFailure对此事件作出处理

    def testConnect(host, port):
         testFactory = ConnectionTestFactory( ) 
         reactor.connectTCP(host, port, testFactory)
         return testFactory.deferred # 返回连接任务的deferred 

    def handleSuccess(result, port):# deferred“事件响应器”:连接任务完成的处理 
        print "Connected to port %i" % port reactor.stop( ) 

    def handleFailure(failure, port): # deferred“事件响应器”:连接任务失败的处理 print "Error connecting to port %i: %s" % ( port, failure.getErrorMessage( )) reactor.stop( ) if __name__ == "__main__": import sys if not len(sys.argv) == 3: print "Usage: connectiontest.py host port" sys.exit(1) host = sys.argv[1] port = int(sys.argv[2]) **connecting = testConnect(host, port)** # 调用函数,返回deferred **connecting.addCallback(handleSuccess, port)** # 建立deferred“事件响应器” **connecting.addErrback(handleFailure, port)** # 建立deferred“事件响应器” reactor.run( ) 

三、创建 client 的套路

第二节说到的两个类,是TCP协议客户端的创建套路(方式)。这个套路拆解如下:

  1. 定义“工厂”和“协议”两个类:
    (1). “协议”类是CallbackAndDisconnectProtocol,“工厂”类是 ConnectionTestFactory 类的名字不重要,但必须正确说明所继承的父类:
    class CallbackAndDisconnectProtocol(protocol.Protocol)
    class ConnectionTestFactory(protocol.ClientFactory) **
    (2). “协议”类是“工厂”类实例化的:
    protocol = CallbackAndDisconnectProtocol;
    (3).只在“工厂”类中有 init 函数,并在其中实例化一个deferred 对象:
    ** self.deferred = defer.Deferred( ) **
    (4)在“工厂”类中,重设父类函数 clientConnectionFailed,通过deferred引发事件,报告连接失败:
    ** self.deferred.errback(reason)

    (5)在“协议”类中,重设父类函数 connectionMade,由对象factory引用“工厂”类中的deferred,经其引发事件,报告连接正常:
    self.factory.deferred.callback("Connected!")
    并由对象transport引发事件,报告连接断开:
    self.transport.loseConnection( );
    上述“对象”,都是从各自父类继承来的。
  2. 在函数testConnect(host, port)中,
    (1).将“工厂”类实例化:
    testFactory = ConnectionTestFactory( )
    (2).由全局循环“主持人”reactor建立以testFactory为“主演”的TCP连接:
    reactor.connectTCP(host, port, testFactory)
    (3)返回deferred对象:
    return testFactory.deferred

至此,一个以事件驱动为基础、异步执行任务的框架程序搭成了。 上述三节的内容,据 Twisted 官方说,是“学习曲线最陡”的部分(They represent the steepest part of the Twisted learning curve.)。
我的感受,造成“最陡”的原因,是由于套路新颖独特,初学乍练不易适应。

  1. 框架对象众多,一时记不牢;
  2. 对象之间的关系比较复杂,一时理不清;
  3. “事件驱动”这种模式,反映在程序文本中,有时见不到明显的函数调用,让人觉得程序的去向不明;

另外,学习方法很重要。如果以学“语言”的习惯来学框架,遇上问题寻根究底,过分追求“水落石出”;或者,依赖教科书、畸重“理论”,忽视 examples 语句、结构和API文档的分析研究,都不利于翻越这段陡坡.我的体验,集中精力地啃嚼主干骨架,不纠緾于细枝末节,这段最陡的上坡路,顶多爬个十天八天的,就能越过去。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容