twisted 以及相关源码

版权声明: https://blog.csdn.net/dashoumeixi/article/details/87927867

官方文档不怎么友好..哈哈哈

自己写了个

总体按照:https://twistedmatrix.com/documents/current/core/howto/servers.html 来写

twisted总体就是一个循环. 下面我会按照 socket编程 的习惯来说明

第一个例子: 里面有2个类

说简单说明一下:

1. Protocol 类, 内部封装了一个  transport 对象, 这个 transport 内部又封装了 socket 对象.

    transort   ->    twisted.internet.tcp.Server .

    综上来说你可以把protocol.transort 认为是一个socket对象, 如果要发送数据可以使用  transport.write

2.  Factory类. 用于创建 一个个 Protocol 对象的,即创建一个个socket对象.(其实不是),下面有源码解释

3. 下面的例子都可以使用telnet 来充当客户端

其他的说明在下面代码中

from twisted.internet.selectreactor import  SelectReactor
from twisted.internet.protocol import  Protocol,Factory
import  time

#这一行可以省略. 可以直接 from twisted.internet import reactor
reactor = SelectReactor()

#Protocol  相当于一个个 socket
class Echo(Protocol):

    # addr 由工厂传递对方 ip 地址
    def __init__(self,addr):
        self.addr = addr
        print('echo init ,peer addr :' , addr)

    
    #重写的函数, 数据一旦到达
    def dataReceived(self, data):
        print('datarecvd :', data)
        self.transport.write(data)

    #socket 一旦 被监听套接字 accept 后
    def connectionMade(self):
        print('connectionMade ' )
        self.transport.write(b'connectionMade')

    #对端关闭
    def connectionLost(self, reason):
        print('connectlost')





#工厂将创建一个个Protocol对象, 你可以把Protocol 对象当作socket来对象
class fa(Factory):
    protocol = Echo #buildProtocol 根据这个类属性来创建protocol
    

    #重写了buildProtocol ,默认源码中并没有传递 addr .其他都一样, 每一次有客户端连接都将调用
    def buildProtocol(self, addr):
        print('buildProtocol : ', addr)
        p = self.protocol(addr)
        p.factory = self
        return p


#listenTcp 先将创建监听套接字,然后用于监听18900端口 ,具体可以看之后的 c 代码解释
reactor.listenTCP(18900,fa(),100)
reactor.run()

#工厂, 即创建一个个 protocol  , 其实socket并不是他创建的,只是用他来管理一个个protocol对象
'''
    源码: 只留了重要的

            #这个clients 内部是真正的socket对象
            clients = _accept(bufferingLogger, range(numAccepts),self.socket,_reservedFD)

            #获取host
             host = socket.getnameinfo(addr, socket.NI_NUMERICHOST |         socket.NI_NUMERICSERV)

            #获取addr
             addr = tuple([host[0]] + list(addr[1:]))
            
            #self.factory 是我们传递进去的工厂, 用来创建一个个protocol对象
             protocol = self.factory.buildProtocol(self._buildAddr(addr))
            
            #transport 是将存放accept返回的socket对象
            transport = self.transport(skt, protocol, addr, self, s, self.reactor)

            #最后把transport存放在protocol 中
            protocol.makeConnection(transport)
'''

一般编写服务器时 (用c来解释):

    int listenfd =  socket(AF_INET, SOCK_STREAM,0) # 创建套接字

    #这部分如果看不懂可忽略
    struct sockaddr_in serv_addr;
    memset(&serv_addr,0,socklen);  
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(12345);
    serv_addr.sin_family = AF_INET;
    #这部分如果看不懂可忽略


     #绑定到本地ip port
    if(bind(listensock,(SA*)&serv_addr,sizeof(serv_addr)) < 0){
        perror("bind");
        return 0;
    }
 
    #转成监听套接字
    if(listen(listensock,BACKLOG) < 0){
        perror("listen");
        return 0;
    }


    #以上部分可以全部忽略, 只看下面的
    while(1){

        #获取socket
        int client_sock = accept(listenfd , ... ) 

        #在twisted中在此将创建一个个protocol,内部存放transport ,transport内部存放socket
        protocol = factory.buildProtocol(... )

        #创建transport对象内部存放 client_sock
        trans = transport( client_sock , .... )
        
        #把transport存放在protocol中
        protocol.makeConnection(transport)

    }

第2个例子:

在上面的例子 直接继承Protocol 或许不太好用,接收到的都是一个个字符

下面改成  LineReceiver ,他也继承自Protocol ,所以你不用担心:

只改了Echo

from twisted.internet.selectreactor import  SelectReactor
from twisted.internet.protocol import  Protocol,Factory

from twisted.internet.endpoints import TCP4ServerEndpoint
from twisted.protocols.basic import LineReceiver
import  time
reactor = SelectReactor()
from  twisted.internet.tcp import Server

class Echo(LineReceiver):
    def __init__(self,addr):
        self.addr = addr
        print('echo init ,  peer addr:', addr)


    #唯一不一样的地方. dataReceived 在LineReceiver自有实现,我们就别去改了
    #重写这个函数,这个函数将返回一行行数据 ,根据\r\n来分割
    def lineReceived(self, line):
        print('lineReceived :' , line)
    
        #发送回去
        self.sendLine(b'echo' + line)
        #发回去后立刻断开连接
        self.transport.loseConnection()


    def connectionMade(self):
        print('connectionMade :' , type(self.transport), self.transport )
        self.transport.write(b'connectionMade')

    def connectionLost(self, reason):
        print('connectlost')

class fa(Factory):
    protocol = Echo

    def buildProtocol(self, addr):
        print('buildProtocol : ', addr)
        p = self.protocol(addr)
        p.factory = self
        return p


reactor.listenTCP(18900,fa(),100)
reactor.run()

第3个例子: 客户端 , 基于上面的服务器 , 除了一个类变了其他都没变

from twisted.internet.selectreactor import SelectReactor
from twisted.internet.protocol import  Protocol,ClientFactory,ReconnectingClientFactory
from twisted.internet.tcp import Connector


#还是没变, 把他看成与服务器通信的socket即可
class Echo(Protocol):
    def __init__(self,addr):
        self.addr = addr
        print('serv addr:', addr)

    def dataReceived(self, data):
        print('from serv:', data)
    def connectionMade(self):
        print('connectionMade :' , type(self.transport), self.transport )
        self.transport.write(b'Client connectionMade\r\n')

    def connectionLost(self, reason):
        print('client connectlost :' , reason)



#这里继承了ClientFactory , ClientFactory 继承了Factory ,只是多了些函数罢了
class ClientFa(ClientFactory):
    protocol = Echo #创建协议(socket)

    #开始连接的时候
    def startedConnecting(self, connector:Connector):
        print('startedConnecting , ' ,connector )
        connector.connect()

    #连接失败
    def clientConnectionFailed(self, connector, reason):
        print('clientConnectionFailed' , connector ,reason)
    
    #连接断开
    def clientConnectionLost(self, connector, reason):
        print('clientConnectionLost' , connector, reason)

    #创建protocol
    def buildProtocol(self, addr):
        p = self.protocol(addr)
        p.factory = self
        return p

reactor = SelectReactor()

#客户端连接服务器
reactor.connectTCP('127.0.0.1',18900,ClientFa())
reactor.run()

defer: deferred对象相当于一个事件 .  一旦触发 (deferrd.callback ) 就会调用注册(addcallback/adderrback)在其中的回调函数

含有  2个回调链, 一个成功, 一个失败;

注意: defer 本身并不是一种异步 ,他只是一个对象用于管理回调函数,   通常情况由reactor 的异步操作来激活defer,运行其中的代码.所以由reactor 参与的defer 才是异步操作; 

可以参阅defer的callback 源代码,其中只是一个小循环来调用各个回调函数;

另外deferred对象只能被激活(callback)一次.下面有代码

from twisted.internet import defer

def success(v):
    print('success : ', v)
def failed(v):
    print('failed : ', v)

d =defer.Deferred()  #创建一个延迟对象
d.addCallback(success) #一旦被激活就调用

d.callback('123')   #激活 ,触发 defer 的回调
print('finished')
from twisted.internet import defer

def success(v):
    print('success : ', v)
def failed(v):
    print('failed : ', v)

d =defer.Deferred()
d.addCallback(success)
d.callback('123')
d.callback('123')    #多了一行 -> 激活 . 将出错. callback只能调用一次
print('finished')
from twisted.internet import defer

def success(v):
    print('success : ', v)
def failed(v):
    print('failed : ', v)

d =defer.Deferred()
d.addCallbacks(success , failed)  #既能增加成功的,也还增加失败的回调
#d.callback('123')     #激活 ,success被运行

d.errback(Exception('你失败了'))  #调用 errback 失败的情况,传递一个Exception,twisted将封装成                    
                                 #Failure

#d.errback  再次调用也将失败, 不论callbak还是errback 都只能调用一次
print('finished')

上面都是先添加回调再激活 , 下面试试先callback 再添加回调

def c1(res):
    print('c1 ' , res)
    return 1
def c2(res):
    print('c2 ', res)
    return 2

def c3(res):
    print('c3 ' , res)
    return  3
d = defer.Deferred()
print('激活 d')
d.callback(0)

print('添加回调')
d.addCallback(c1)
d.addCallback(c2)
d.addCallback(c3)

上面是defer 的简单介绍, 下面与reactor 配合使用:

def success(v):
    print('success : ', v)
    reactor.stop()

def failed(v):
    print('failed : ', v)
    reactor.stop()

d =defer.Deferred()
d.addCallbacks(success , failed)  #添加回调

reactor.callWhenRunning(d.callback,'123') #运行就激活callback
reactor.run()
def success(v):
    print('success : ', v)


def failed(v):
    print('failed : ', v)

def end(v):
    print('stopit :' , v)
    reactor.stop()

d =defer.Deferred()

d.addCallbacks(success , failed)
d.addBoth(end)                  #分别往callback 与 errback中添加

reactor.callWhenRunning(d.callback,'123')
reactor.run()

关于defer中的阻塞: 真正异步的是reactor,defer是一种事件

def success(v):
    print('success : ', v)


def failed(v):
    print('failed : ', v)

def block(_):        #添加一个阻塞函数 , 不要以为在defer中随意添加就是非阻塞了
    print('start-block')
    import time
    time.sleep(2)
    print('blockend')


def end(v):
    print('stopit :' , v)
    reactor.stop()

d =defer.Deferred()

d.addCallbacks(success , failed)
d.addCallback(block)
d.addBoth(end)              

reactor.callWhenRunning(d.callback,'123')
reactor.run()

defer异常处理 : 往往defer 的callbak 与 errback 成对出现, 如果

你只加了一个callback 则相对应的errback是一个pass.

例如:   d.addCallback(success) 对应的errback是一个pass.

       现在: d.addErrback(failed) 这个错误处理是在第2层.  一旦success有异常, failed将捕获

比如下面的例子:
 

def success(v):
    print('success : ', v)


def failed(v):
    print('failed : ', v)
    raise Exception('nihao')    #raise异常

def end(v):
    print('end:' , v)

    return 123  #返回一个正常值 / None,不再抛异常

def done(v):
    print('done : ', v)
    reactor.stop()

d =defer.Deferred()

d.addCallbacks(success , failed) #成对
d.addErrback(failed)            #在第一个failed后又加一个failed
d.addBoth(end)                  #2边都加了
d.addCallback(done)            #在callbak一端加了done

'''
    现在: callback链 : success -> pass -> end -> done
          errback  :  failed -> failed -> end -> pass
'''

#首先 第一个failed被激活 -> 传递给第2个failed -> 传递给 end ,内部返回一个值,不再raise->
# 由于不在raise 回到了 callbak链 中的 done . 结束
reactor.callWhenRunning(d.errback,Exception('start'))
reactor.run()

再看一个例子: 引发的异常总是由下一层的errback 捕获

def success(v):
    print('success : ', v)
    raise  Exception('start')

def failed(v):
    print('failed : ', v)    
    return v                #第一层的errback并没有调用

def failed2(v):            #第2层的捕获了, 并返回异常,由下一层处理异常
    print('failed 2 : ',v)
    return v

def failed3(v):            #第3层异常 返回了一个123,意味着异常不再传播下去
    print('failed 3', v)
    return 123        #or None

def done(v):                #第4层的callbck 将被调用
    print('done :' , v)
    reactor.stop()
d =defer.Deferred()

d.addCallbacks(success , failed)
d.addErrback(failed2)
d.addErrback(failed3)
d.addCallback(done)

'''
第1层 : success failed
2     : pass    failed2
3     :pass     failed3    -> 将返回一个 123(or None) 不再往下一层传播异常,所以done被调用
4     : done    pass 
    
'''

reactor.callWhenRunning(d.callback,1)
reactor.run()

一个客户端的例子:

reactor = SelectReactor()
from twisted.internet.protocol import defer
class cli_pro(Protocol):
    def __init__(self,addr):
        print('cli_pro : ', addr)
    def connectionMade(self):
        print('connectionMade')
        self.factory.d.callback('ok') #一旦连接成功  , 触发回调,这些都是异步操作

class cli_fa(ClientFactory):
    protocol = cli_pro
    def __init__(self):
        self.d  = defer.Deferred()

    def startedConnecting(self, connector: Connector):
        print('startedConnecting , ', connector)

    def clientConnectionFailed(self, connector, reason):
        print('clientConnectionFailed', connector, reason)
        self.d.errback(reason)    #出错回调,异步回调错误的函数

    def buildProtocol(self, addr):
        p = self.protocol(addr)
        p.factory = self
        return p


# 返回defer对象, 用于添加回调
def start_conn(host,port):
    f = cli_fa()
    reactor.connectTCP(host,port,f)
    return f.d

def success(result,port):
    print('success result:' , result, port)
    reactor.stop()

def failed(reason,port):
    print('failed reason:' , reason, port)
    reactor.stop()

d = start_conn('127.0.0.1',80)# www.baidu.com
d.addCallback(success,80)
d.addErrback(failed,80)
reactor.run()

下面说一下 :   maybeDeferred . 并且把以上的东西做一个汇总. 

maybeDeferred 用于包装这样一种情况:

some_dict = {}

def x(key):
    if key in some_dict:
        return some_dict[key]
    
    d = Deferred()
    d.addCallback(...)
    return d


def end(_):
    print('end')
''' 
 如果返回的是 Deferred 对象则原样返回, 否则返回一个激活的全新的Deferred
 按上面的代码, 如果返回 some_dict[key] , 则相当于:
    
 d  =  Deferred()
 d.callback(some_dict[key])
 return d


'''
d = maybeDeferred(x,'123') 
d.addCallback(end)

基本上是这样的, 实现一个服务器, 一个客户端.

客户端很简单,一个工厂一个协议, 一旦连接到服务器就发送一个例如 www.baidu.com 让服务器返回 ip 地址.

服务器的思路: 一个工厂一个协议 用于接受客户端发送的host , 同时在工厂中存放一个dict,把host作为key,ip为value.

如果dict中存在则直接返回给客户端,如果没有则去连接此host,从而获取ip.

既然要去连接其他服务器, 那么服务端的中必然又要额外创建一个工厂一个协议, 同时在协议中触发 callback,然后在

工厂中的dict放置ip,host ,再发送给客户端. 

具体代码如下:

serv.py

from twisted.internet.selectreactor import  SelectReactor
from twisted.internet.protocol import  Protocol,Factory,ServerFactory,ClientFactory,defer
from twisted.protocols.basic import LineReceiver
import  time
from  twisted.internet.tcp import Server


reactor = SelectReactor() #可省略

#服务端 client_socket
class myProcotol(Protocol):
        
    #用于接受客户端发送来的host
    def dataReceived(self, data):
        host = str(data,encoding='utf8') #编码问题,注意一下

        print('data recved:', host)

        # 如果 kw 中存在则直接写回给客户端 , 然后关闭连接
        d = defer.maybeDeferred(self.factory.start_conn_host , host)
        d.addCallback(self.transport.write)

        #注意,由于回调都会传入参数, 这里直接写个lambda
        d.addBoth(lambda _ : self.transport.loseConnection())

#服务端 ,用于创建客户端socket
class myServer(ServerFactory):
    protocol = myProcotol

    #缓存
    kw = {}

    #设置缓存
    def setdata(self,ip , host):
        print('set data : ' , ip , host)
        self.kw[host] = ip
        print('现在缓存中有:' , self.kw)
        return bytes(ip,encoding='utf8')   #想想为什么?? 答案:这个函数是第一个回调

    def start_conn_host(self,host):
        v = self.kw.get(host, None)
        if v :
            return bytes(v,encoding='utf8')  #如果缓存中存在,直接把这个值当成result

        #如果缓存中不存在, 则去获取
        print('start_conn_host :' ,host)
        d = defer.Deferred()

        #一旦获取成功, 则加入第一个回调
        d.addCallback(self.setdata,host)
        fa = ClientFa(d)
        reactor.connectTCP(host, 80, fa)
        return d    #返回延迟对象


#用于获取 ip 地址的 socket
class clientProtocol(Protocol):

        
    def connectionMade(self):

        #从socket对象中获取 ip 地址
        self.ip = self.transport.getPeer().host
        print('获取服务器地址 :' ,self.ip)
        self.transport.loseConnection()

    def connectionLost(self, reason):
        print('完成, 开始回调')
        #触发回调
        self.factory.d.callback(self.ip)


#用于获取ip的工厂
class ClientFa(ClientFactory):
    protocol = clientProtocol

    def __init__(self , d):
        self.d = d # Deferred 对象

    def startedConnecting(self, connector):
        print('开始连接 ')

    def clientConnectionFailed(self, connector, reason):
        print('clientConnectionFailed' , connector ,reason)

        #如果失败则触发异常链
        self.d.errback(reason)


reactor.listenTCP(18900,myServer(),100)
reactor.run()

client.py  客户端我就不多说了, 应该都能看懂

from twisted.internet.selectreactor import SelectReactor
from twisted.internet.protocol import  Protocol,ClientFactory,ReconnectingClientFactory
from twisted.internet.tcp import Connector
from twisted.internet.protocol import defer
import sys
class Echo(Protocol):
    def __init__(self,addr):
        self.addr = addr
        print('serv addr:', addr)

    def dataReceived(self, data):
        print('from serv:', data )

    def connectionMade(self):
        print('connectionMade :' )
        q = 'www.baidu.com'
        self.transport.write(bytes(q,encoding='utf8'))



class ClientFa(ClientFactory):
    protocol = Echo
    def __init__(self):
        self.d = defer.Deferred()

    def startedConnecting(self, connector:Connector):
        print('startedConnecting' )


    def clientConnectionFailed(self, connector, reason):
        print('clientConnectionFailed' )
        reactor.stop()

    def clientConnectionLost(self, connector, reason):
        print('clientConnectionLost' )
        reactor.stop()



    def buildProtocol(self, addr):
        p = self.protocol(addr)
        p.factory = self
        return p


reactor = SelectReactor()
reactor.connectTCP('127.0.0.1',18900,ClientFa())
reactor.run()



关于  inlineCallbacks : 这里需要生成器的知识, 我的博客里有好几篇说了生成器,

比如:python 生成器进阶    生成器,send  等等, 你自己可以去搜一下

inlineCallbacks  是创建 Deferred 的另一种方式. 或者说是一种更好的方式, 而不是向之前那种无穷的回调中

先看下面第一个例子 再具体说明:

@defer.inlineCallbacks    # 唯一比py中生成器多了个装饰器
def sm_callback():
    print('1.begin')
    r = yield 1           # 产出 1 

    print('2. r :' , r)  # 1 被传递进来了

    r = yield 2          # 产出 2
    
    print('3. r:' , r)   # 2 又被传递进来了, 能想起来 deferred 中的回调参数传递吗?

    reactor.stop()


reactor.callWhenRunning(sm_callback)
reactor.run()

上面的例子 就结果论来说 , 与Deferred中的addcalback类似.

做个对比:

def f1( res ):
    print('f1 start  就像 sm_callback 中的第一行')
    return 1    #相当于 yield 1
def f2(res) :
    print('f2 res :' , res) #相当于 print('2. r :' , r)    
    return 2   
def f3(res):
    print('f3 res :' , res)


d = defer.Deferred()
d.addCallback(f1)
d.addCallback(f2)
d.addCallback(f3)
d.callback(None)

对比一下, 是不是  inlineCallbacks 的代码要清爽多了.反正我是这么觉得的,

注意 : inlineCallbacks 装饰的函数返回是一个激活的Deferred对象. 意味着sm_callback() 一调用,内部代码就运行起来了

 *** 再具体说明一下 inlineCallbacks 装饰的函数: 

1. 由每个 yield 分割的地方就相当于 一个callback

2. 虽然 函数中有 yield 在python 中是一个生成器, 但在这里他将返回一个Deferred, 当然, 内部还是迭代生成器,去看源码就知道了,

    生成器的方式与callback从形式上是一致的

3.再次强调. 就函数本身来说就是一个生成器对象, 经过装饰后将返回一个激活的Deferred对象 . (与返回值有关,见下面的例子)

再看一个例子:  由于回调链 中可以 返回一个Deferred , 因此 也可以在 inlineCallbacks  实现.

注: 如果在回调链return Deferred()  , 那么 外层回调链将在内层Deferred的回调链完成后继续调用,否则

将暂停.

@defer.inlineCallbacks
def sm_callback():
    print('begin')

    #在Deferred中返回 Deferred
    d = defer.Deferred()

    reactor.callLater(2,  d.callback , 100)  #内部的Deferred 需要我们自己去激活

    r = yield d   # 这个返回是什么呢??

    print('r :'  ,r)

    d = defer.Deferred()
    reactor.callLater( 3, d.errback,  Exception('3秒后发生异常'))

    try:
        r = yield d
    except Exception as e:
        print('异常:' , e)


    reactor.stop()

reactor.callWhenRunning(sm_callback)
reactor.run()

关于返回值:

在addCallback 的过程中, 我们都是直接返回 return xxx;由下一个回调去接受result. 

下面用inlinecallbacks 来做同样的事.

注意一点,就函数本身来说是一个生成器对象,经过装饰后返回一个 Deferred对象.

生成器中 return 123 , 这个返回值将被收集在StopIteration.value 中: 具体可以看yield from 生成器 协程

因此在twisted中 需要使用  defer.returnValue 来把返回值带回来, 源码中就是抛一个异常,终止了生成器对象,并在外面

捕获了异常中的返回值,然后在返回值传递到下一个callback中


@defer.inlineCallbacks
def cb1():
    print('cb1 的第一个回调')
    d = defer.Deferred()
    reactor.callLater(2, d.callback, 'first') 

    r = yield d
    print('cb1 的第2个回调 , result:' , r)
    d = defer.Deferred()
    reactor.callLater(1,d.callback,'sec')

    r = yield d
    print('cb1 的第3个回调 , r:' , r)

    defer.returnValue('im done')      #这里不能使用 return 'im done'. 因为此函数是一个生成器


def done(res):
    print('done :' , res)
    reactor.stop()

r = cb1()  #调用后返回一个Deferred对象
r.addCallback(done)   #添加回调
reactor.run()

关于inlineCallbacks 异常 . 把上面的例子稍作修改:

reactor = SelectReactor()
@defer.inlineCallbacks
def cb1():
    print('cb1 的第一个回调')
    d = defer.Deferred()
    reactor.callLater(2, d.callback, 'first')

    r = yield d
    print('cb1 的第2个回调 , result:' , r)
    d = defer.Deferred()
    reactor.callLater(1,d.callback,'sec')

    r = yield d
    print('cb1 的第3个回调 , r:' , r)

    raise Exception('出错了')      #修改这一行 



def done(res):
    print('done :' , res)
    reactor.stop()

def err(res):   #添加了一个出错的处理
    print('err:' , res)
    reactor.stop()


r = cb1() #r 是激活的Deferred对象.  cb1() 即开始调用,与reactor.callWhenRunning(cb1)一样
r.addCallbacks(done,err)  #成功调用done, 失败调用err
reactor.run()


'''
到第3个回调的时候 raise Exception . 
由于 又添加了一对回调 
因此异常被传递到了 err.
源码中一旦有异常则调用 d.errback()

'''

对于inlineCallbacks , 不论是正常的返回值(把值传递给下个回调),还是异常(进入异常链), 都遵循着callback这种方式.唯一跟callback不同的就是代码的形式.

  当然  inlineCallbacks 的名字也暗含着,这个函数内可以有N 个回调函数的意思.

  在实际使用中,我自己常常把2种形式混合着用,哪个用的舒服用哪个.

关于: DeferredList

往往不会只有一个Deferred对象. 如果你需要用到N个Deferred,并且需要他们完后收到通知的话,就需要DeferredList.

下面的代码中有跟async模块做对比.

先看一个简单的例子:


def done(res):
    print('done :' , res)

dl = defer.DeferredList([]) #你可以把DeferredList看成一个Deferred对象,他继承了Deferred
dl.addCallback(done)
#dl.callback(None) 别再调用callback,他已经激活了 . 如果[]是空的话,否则不会激活
'''
创建2个DEFERRED 对象. 加入进list
'''
reactor = SelectReactor()

def done(res):
    print('done :' , res)

d1 = defer.Deferred() 
d2 = defer.Deferred()
dl = defer.DeferredList([d1,d2]) #顺序 d1, d2, 输出顺序也将是d1,d2的结果
dl.addCallback(done)    #list 加入回调,与一般的DEFERRED 一样


d2.callback('im d2') #先触发 d2 
reactor.callLater(2,d1.callback,'im d1') #2秒后再触发d1 , 如果把这行注释掉,list将永远不会触发

reactor.run()

把上面的带与asyncio模块的 asyncio.wait做个对比: 这部分如果没学过asyncio的 不看也没关系


#相当于callback
async def func(i):
    print('start')
    await asyncio.sleep(i) #交出控制权,事件循环执行下个任务,同时等待完成
    return i
 



'''
    asyncio.async(func(i)) -> 相当于创建一个Deferred对象
   
    tasks -> 这个列表就相当于DeferredList

    asyncio.wait(tasks) - > 相当于上面代码中的 done 函数用于返回结果
'''
tasks = [asyncio.async(func(i)) for i in range(3)]  #开始调度一个Task对象列表




#下面的代码无关
lp = asyncio.get_event_loop()
lp.run_until_complete(asyncio.wait(tasks))
for task in tasks:
    print(task.result())
DeferredList 的执行顺序:
reactor = SelectReactor()

def d1_func(res):
    print('d1_func :' ,res)
    return 'd1'

def d2_func(res):
    print('d2_func :' ,res)
    return 'd2'

def done(res):
    print('done :' , res)

d1 = defer.Deferred()
d2 = defer.Deferred()
dl = defer.DeferredList([d1,d2])
dl.addCallback(done)

d2.addCallback(d2_func)  #加入了回调,查看执行顺序
d2.callback('im d2')     #先调用d2

d1.addCallback(d1_func)  
reactor.callLater(2,d1.callback,'im d1') 

reactor.run()

可以看到,Deferred 本身还是callback()就运行代码了, 而 DeferredList 的回调 done ,是需要所有 Deferred 对象都激活后才会调用.

就像asyncio.wait 将等待所有的futrue对象全部运行完才会返回一样,不论成功还是失败.

关于其中一个或多个Deferred的异常: 

def done(res):
    print('done :' , res)

d1 = defer.Deferred()
d2 = defer.Deferred()


dl = defer.DeferredList([d1,d2],consumeErrors=True) #如果把这个参数注释将直接报错
dl.addCallback(done)


d2.errback(Exception('d2 失败了'))
d1.callback('im d1')

自己处理异常:

def err(res):
    print('err :' , res)

def done(res):
    print('done :' , res)

d1 = defer.Deferred()
d2 = defer.Deferred()


dl = defer.DeferredList([d1,d2],consumeErrors=False)
dl.addCallback(done)

d2.addErrback(err) #唯一增加的代码
d2.errback(Exception('d2 失败了'))
d1.callback('im d1')

在err中继续抛异常会怎么样? 


def err(res):
    print('err :' , res)
    raise res

还会继续报错. 

再换个方式 来处理异常 ,这里比较有意思,下面会说源码的处理方式:

def err(res):
    print('err :' , res)
    raise res

def done(res):
    print('done :' , res)

d1 = defer.Deferred()
d2 = defer.Deferred()


d2.addErrback(err) #把这行代码移动到这里
dl = defer.DeferredList([d1,d2],consumeErrors=True) #这个参数再次改成True
dl.addCallback(done)

d2.errback(Exception('d2 失败了'))
d1.callback('im d1')

这样就不会再次报错了. 原因是在源码中,创建 DeferredList 时,将默认给每个 Deferred 都添加一对回调,那么根据上面代码,

d2的错误回调链 err -> 默认回调 , 默认回调将判断是否异常,如果异常则判断 consumeErrors 是否是True 如果是 return None

因此,异常处理完毕..

同时,在之前的DeferredList 所有代码中,我留了个错误. d1与 d2的回调, 他们的返回值没有被传递进最终的done中,

原因还是一样, 只有当你在 创建 DeferredList 之前 ,先把回调都加好,这样就行了,否则, DeferredList 会先加一层回调.

因此, 如果你想使用 Deferred 回调的返回值 则先在 DeferredList 之前加好..

取消: cancel函数基本上就是调用异常(err)链

def f1(res):
    print('f1 :' ,res)

def err(res):
    print('err :' , res)

d = defer.Deferred()
d.addCallback(f1)
reactor.callLater(1,d.callback)
d.cancel() #取消. 

上面代码将直接抛异常.


def f1(res):
    print('f1 :' ,res)

def err(res):
    print('err :' , res)
d = defer.Deferred()
d.addCallbacks(f1,err)  #加入错误处理链
reactor.callLater(1,d.callback)
d.cancel()#再次取消

上面代码直接调用异常链.

先激活再取消/先取消再激活:

def f1(res):
    print('f1 :' ,res)

def err(res):
    print('err :' , res)
    
d = defer.Deferred()
d.addCallbacks(f1,err) #加入回调链

d.callback('callback') #先激活 , 或者先取消再激活
d.cancel()#取消

取消仅仅是一个请求,一旦激活后再取消就无效了. 如果没有激活先取消,则调用异常链.具体可看cancel源码

构造函数中的参数: 抢占默认的操作

def f1(res):
    print('f1 :' ,res)

def err(res):
    print('err :' , res)

def cancel_func(d):  #此函数将先被调用
    print('cancel_func :' , d) 
    d.callback('callback')  #可以在内部再次激活回调,抢占默认cancel操作.
    #或者 d.errback(Exception('exception')) ,来抢占默认操作

d = defer.Deferred(cancel_func) #构造函数中传递一个取消函数.一旦取消将先调用此函数
d.addCallbacks(f1,err)

d.cancel() #取消

总之对于取消操作 : 如果先激活了,再取消就无效了 .或者先取消了,再激活也没意义了. 取消是一个请求,并不会真正的

去终止这个操作,Deferred内部的代码还将继续运行,只是结果被忽略了.

def do(d):
    print('start to do')
    d.callback('im callback')

def do_work():
    d =defer.Deferred()
    reactor.callLater(5,do,d)  #5秒后执行激活操作
    return d

def done(res):
    print('done :' , res)
def err(res):
    print('err :' , res)

d = do_work()
d.addCallbacks(done,err) 
reactor.callLater(2,d.cancel) #2秒后取消

reactor.run()

由上面例子可以知道,就算cancel 执行后, callback 还将继续, 只是把最终的结果忽略了;

真正的取消:

def do(d):
    print('start to do')
    d.callback('im callback')



def do_work():
    def cancel_func(d):  #closure 函数
        print('cancel invoke')
        obj.cancel()  #利用calllater 返回的接口中有cancel这个接口
 
    d =defer.Deferred(cancel_func)  #传入闭包函数
    obj = reactor.callLater(5,do,d)
    return d

def done(res):
    print('done :' , res)
def err(res):
    print('err :' , res)

d = do_work()
d.addCallbacks(done,err)
reactor.callLater(2,d.cancel)

reactor.run()

模拟内层异常效果:

@defer.inlineCallbacks
def func_1():
    print('start')
    d = defer.Deferred()
    reactor.callLater(5,d.callback,'hello') #5秒后激活
    try:
        res = yield d
    except Exception as e:
        raise


def done(res):
    print('done :' , res)
def err(res):
    print('err :' , res)

d = func_1()  #创建一个Deferred对象
d.addCallbacks(done,err) #加入回调链
reactor.callLater(2,d.cancel)  #2秒后调用取消
reactor.run()

上面代码的效果就是 先运行外层Deferred, 5秒后激活内部Deferred , 2秒后取消外层Deferred.

一旦外层被取消,内层的也将被取消;

一个取消的例子 , 与上面其中一份代码类似:

from twisted.python.failure import Failure
class serv_protocol(Protocol):
    def connectionMade(self):
        self.d = None
    def dataReceived(self, data):
        host = str(data, encoding='utf8')
        print('data recved:', host)
        self.d = start_conn_host(host)
        self.d.addCallbacks(self.transport.write,self.err_callback) 
        self.d.addBoth(lambda _ :self.transport.loseConnection())
    def err_callback(self,exception:Failure):
        print('err:' , exception)
        self.transport.write(bytes(exception.getErrorMessage(),encoding='utf8'))


    def connectionLost(self, reason):
        print('conn lost : ' , reason)
        #加入取消
        if self.d:
            d , self.d = self.d , None
            d.cancel()


class serv_factory(ServerFactory):
    protocol =  serv_protocol
    kw = {}
    @staticmethod
    def setdata(ip, host):
        serv_factory.kw[host] = ip
        print('setdata :' , ip , host)
        return bytes(ip,encoding='utf8')

class client_protocol(Protocol):

    def connectionMade(self):
        self.ip = self.transport.getPeer().host
        print('获取到ip:' , self.ip)
        self.transport.loseConnection()
    def connectionLost(self, reason):
        if self.factory.d:
            d , self.factory.d = self.factory.d, None
            d.callback(self.ip)

class client_factory(ClientFactory):
    protocol =  client_protocol
    def __init__(self,deferred,host):
        self.d = deferred
        self.host = host
    def clientConnectionFailed(self, connector, reason):
        if self.d:
            d, self.d = self.d, None
            d.errback(reason)

def start_conn_host(host):
    def cancel_func(d):
        print('取消') 
        cf.d = None  #防止在client_factory.clientConnectionFailed 以其他形式的错误触发
        conn.disconnect()  #真正的断开socket
    ip = serv_factory.kw.get(host,None)
    if ip:
        return defer.succeed(ip)
    d = defer.Deferred(cancel_func)
    d.addCallback(serv_factory.setdata,host)
    cf = client_factory(d, host)
    conn = reactor.connectTCP(host,80,cf)
    return d


reactor.listenTCP(18900,serv_factory(),100)
reactor.run()                        

总之: defer的取消无法真正的取消,该执行的代码(callback,errback)依旧执行只是最终忽略结果.通过 connectTCP 返回的连接器(内部封装了socket),来真正的断开socket.

上面代码中在 disconnect之前 , 把cf.d = None,的目的是

1.disconnect 会引起  clientConnectionFailed的调用. 

2.cancel() 这个调用最后会调用errback , 如果不把 cf.d = None 的话, 就会引起2此deferred调用.

猜你喜欢

转载自blog.csdn.net/dashoumeixi/article/details/87927867
今日推荐