twisted 诗歌客户端代码分析:嵌套着的多层deferred。

来源:互联网 发布:星际2 不平衡 知乎 编辑:程序博客网 时间:2024/05/18 17:03

http://krondo.com/?p=2159

http://blog.sina.com.cn/s/blog_704b6af70100qay3.html

花了很长的时间才看懂了这段代码的运行过程。该代码是上面两个网页中的twisted 教程中的第13部分的内容,旨在使用嵌套式的deferred建立一个诗歌下载客户端。

这篇文章的主要难点在于对多层deferred的callback函数的调用顺序的理解。


1.服务器提供诗歌的下载和诗歌的转换,两个功能。

2.该客户端首先从服务器上面下载诗歌,之后再使用服务器,对诗歌进行转换。

3.建立两个protocol,两个Factory.

4.PoetryProtocol与PoetryClientFactory用于下载诗歌。

5.TransformClientProtocol与TransformClientFactory用于对诗歌进行转换。

6.在主函数main中进行了三次添加callback。分别为:

d.addCallback(try_to_cummingsify)

d.addCallbacks(got_poem,poem_failed)

d.addBoth(poem_done)

7.在get_poetry(host,port)中为外部的deferred生成处。该函数建立TCP链接,从服务器端下载诗歌。

如果下载成功则在poem_finished中调用callback。则try_to_cummingsify开始执行。

否则,在clientConnectionFailed中调用errback。则首先pass_through然后直接执行poem_failed,再执行peom_done结束。

8.在try_to_cummingsify中建立了一个内部deferred,并且建立TCP链接,传输当前poem到服务器,然后接受转换好了的poem。并且向内部的deferred添加了一个callback

d.addErrback(fail)

9.如果cummingsify成功,则调用handlePoem,在handlePoem调用callback。由于内部deferred中之添加了fail作为errback。因此这个callback是直接pass_through的。

10.如果cummingsify失败,则调用clientConnectionLost,在其中调用d.errback,此时实际上执行了fail函数。

11.这时候外层的try_to_cummingsify函数执行完毕。开始执行got_poem。如果cummingsify成功,这时的poem为cum后的poem。否则为原poem,因为fail函数返回原poem。

12.最后执行poem_done,结束。


源代码如下:

# This is the Twisted Get Poetry Now! client, version 6.0import optparse, sysfrom twisted.internet import deferfrom twisted.internet.protocol import Protocol, ClientFactoryfrom twisted.protocols.basic import NetstringReceiverdef parse_args():    usage = """usage: %prog [options] [hostname]:port ...This is the Get Poetry Now! client, Twisted version 6.0Run it like this:python get-poetry.py xform-port port1 port2 ...If you are in the base directory of the twisted-intro package,you could run it like this:python twisted-client-6/get-poetry.py 10001 10002 10003to grab poetry from servers on ports 10002, and 10003 and transformit using the server on port 10001.Of course, there need to be appropriate servers listening on thoseports for that to work."""    parser = optparse.OptionParser(usage)    _, addresses = parser.parse_args()    if len(addresses) < 2:        print parser.format_help()        parser.exit()    def parse_address(addr):        if ':' not in addr:            host = '127.0.0.1'            port = addr        else:            host, port = addr.split(':', 1)        if not port.isdigit():            parser.error('Ports must be integers.')        return host, int(port)    return map(parse_address, addresses)class PoetryProtocol(Protocol):    poem = ''    def dataReceived(self, data):        self.poem += data    def connectionLost(self, reason):        self.poemReceived(self.poem)    def poemReceived(self, poem):        self.factory.poem_finished(poem)class PoetryClientFactory(ClientFactory):    protocol = PoetryProtocol    def __init__(self, deferred):        self.deferred = deferred    def poem_finished(self, poem):        if self.deferred is not None:            d, self.deferred = self.deferred, None            d.callback(poem)    def clientConnectionFailed(self, connector, reason):        if self.deferred is not None:            d, self.deferred = self.deferred, None            d.errback(reason)class TransformClientProtocol(NetstringReceiver):    def connectionMade(self):        self.sendRequest(self.factory.xform_name, self.factory.poem)    def sendRequest(self, xform_name, poem):        self.sendString(xform_name + '.' + poem)    def stringReceived(self, s):        self.transport.loseConnection()        self.poemReceived(s)    def poemReceived(self, poem):        self.factory.handlePoem(poem)class TransformClientFactory(ClientFactory):    protocol = TransformClientProtocol    def __init__(self, xform_name, poem):        self.xform_name = xform_name        self.poem = poem        self.deferred = defer.Deferred()    def handlePoem(self, poem):        d, self.deferred = self.deferred, None        d.callback(poem)    def clientConnectionLost(self, _, reason):        if self.deferred is not None:            d, self.deferred = self.deferred, None            d.errback(reason)    clientConnectionFailed = clientConnectionLostclass TransformProxy(object):    """I proxy requests to a transformation service."""    def __init__(self, host, port):        self.host = host        self.port = port    def xform(self, xform_name, poem):        factory = TransformClientFactory(xform_name, poem)        from twisted.internet import reactor        reactor.connectTCP(self.host, self.port, factory)        return factory.deferreddef get_poetry(host, port):    """Download a poem from the given host and port. This functionreturns a Deferred which will be fired with the complete text ofthe poem or a Failure if the poem could not be downloaded."""    d = defer.Deferred()    from twisted.internet import reactor    factory = PoetryClientFactory(d)    reactor.connectTCP(host, port, factory)    return ddef poetry_main():    addresses = parse_args()    xform_addr = addresses.pop(0)    proxy = TransformProxy(*xform_addr)    from twisted.internet import reactor    poems = []    errors = []    def try_to_cummingsify(poem):        d = proxy.xform('cummingsify', poem)        def fail(err):            print >>sys.stderr, 'Cummingsify failed!'            return poem        return d.addErrback(fail)    def got_poem(poem):        print poem        poems.append(poem)    def poem_failed(err):        print >>sys.stderr, 'The poem download failed.'        errors.append(err)    def poem_done(_):        if len(poems) + len(errors) == len(addresses):            reactor.stop()    for address in addresses:        host, port = address        d = get_poetry(host, port)        d.addCallback(try_to_cummingsify)        d.addCallbacks(got_poem, poem_failed)        d.addBoth(poem_done)    reactor.run()if __name__ == '__main__':    poetry_main()



原创粉丝点击