Twisted

作者: 东皇Amrzs | 来源:发表于2015-07-28 17:21 被阅读2589次

本文章通过实际例子和用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文档的分析研究,都不利于翻越这段陡坡.我的体验,集中精力地啃嚼主干骨架,不纠緾于细枝末节,这段最陡的上坡路,顶多爬个十天八天的,就能越过去。

相关文章

网友评论

      本文标题:Twisted

      本文链接:https://www.haomeiwen.com/subject/uqmmqttx.html