官方文档不怎么友好..哈哈哈
自己写了个
总体按照: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调用.