zoukankan      html  css  js  c++  java
  • Twisted网络编程必备(2)

    转自:http://www.yybug.com/read-htm-tid-15324.html

    使用异步的方式工作的结果

    除了反应器reactor之外,Deferred可能是最有用的Twisted对象。你可能在Twisted程序中多次用到Deferred,所有有必要了解它是如何工作的。Deferred可能在开始的时候引起困惑,但是它的目的是简单的:保持对非同步活动的跟踪,并且获得活动结束时的结果

    Deferred可以按照这种方式说明:可能你在饭店中遇到过这个问题,如果你在等待自己喜欢的桌子时,在一旁哼哼小曲。带个寻呼机是个好主意,它可以让你在等待的时候不至于孤零零的站在那里而感到无聊。你可以在这段时间出去走走,到隔壁买点东西。当桌子可用时,寻呼机响了,这时你就可以回到饭店去你的位置了。

    一个Deferred类似于这个寻呼机。它提供了让程序查找非同步任务完成的一种方式,而在这时还可以做其他事情。当函数返回一个Deferred对象时,说明获得结果之前还需要一定时间。为了在任务完成时获得结果,可以为Deferred指定一个事件处理器。

    当编写一个启动非同步操作的函数时,返回一个Deferred对象。当操作完成时,调用Deferred的callback方法来返回值。如果操作失败,调用Deferred.errback函数来跑出异常。例子2-4展示了程序使用Deferred使用非同步操作的连接例子,连接一个服务器的端口。

    当调用一个可以返回Deferred的函数时,使用Deferred.addCallback方法指定返回结果时调用的函数。使用Deferred.addErrback方法指定执行发生异常时调用的函数。

    代码
    from twisted.internet.selectreactor import SelectReactor
    from twisted.internet.protocol import Protocol,ClientFactory,defer
    reactor
    = SelectReactor()
    class CallbackAndDisconnectProtocol(Protocol):
    def connectionMade(self):
    self.factory.deferred.callback(
    "Connected!")
    class ConnectionTestFactory(ClientFactory):
    protocol
    =CallbackAndDisconnectProtocol
    def __init__(self):
    self.deferred
    =defer.Deferred()
    def clientConnectionFailed(self,connector,reason):
    self.deferred.errback(reason)
    def testConnect(host,port):
    testFactory
    =ConnectionTestFactory()
    reactor.connectTCP(host,port,testFactory)
    return testFactory.deferred
    def handleSuccess(result,port):
    print "Connect to port %i"%port
    reactor.stop()
    def handleFailure(failure,port):
    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)
    connecting.addCallback(handleSuccess,port)
    connecting.addErrback(handleFailure,port)
    reactor.run()

    运行这个脚本并加上服务器名和端口这两个参数会得到如下输出:

    $ python connectingtest.py oreilly.com 80

    Connecting to port 80.

    或者,如果连接的端口是关闭的:

    $ python connectiontest.py oreilly.com 81

    Error connecting to port 81: Connection was refused by other side: 22: Invalid argument.

    或者连接的服务器并不存在:

    $ python connectiongtest.py fakesite 80

    Error connecting to port 80: DNS lookup failed: address 'fakesite' not found.

    它是如何工作的?

    类ConnectionTestFactory是ClientFactory的子类,并且含有Deferred对象作为属性。当连接完成时,CallbackAndDisconnectProtocol的connectionMade方法会被调用。connectionMade会调用self.factory.deferred.callbakc加上任意值标识调用成功。如果连接失败,ConnectionTestFactory的clientConnectionFailed方法会被调用。第二个参数clientConnectionFailed的reason是twisted.python.failure.Failure对象,封装了异常,并描述了异常发生的原因。clientConnectionFailed传递了Failure对象到self.deferred.errback来标识操作失败。

    testConnecn函数需要两个参数host和port。用于创建叫做testFactory的ConnectionTestFactory的类实例,并且传递给reactor.connectTCP参数host和port。当连接成功时返回testFactory.deferred属性,就是用于跟踪的Deferred对象。

    例子2-4夜展示了两个事件处理器函数:handleSuccess和handleFailure。当从命令行运行时,它们需要host和port两个参数来调用testConnect,指定了结果Deferred到变量connecting。此时可以使用connecting.addCallback和connecting.addErrback来设置事件处理器函数。在每一种情况下,传递port作为附加参数。因为扩展的参数或者关键字参数将会由addCallback或addErrback传递给事件处理器,这将会在调用handleSuccess和handleFailure时得到端口参数作为第二个参数。

    Tip:当函数返回Deferred时,可以确定事件调用了Deferred的callback或者errback方法。否则代码将永远等待结果。

    在调用了testConnect和设置事件处理器之后,控制劝将会交给reactor.run()。依赖于testConnect的执行成功与否,将会调用handleSuccess或者handleFailure,打印西那个关的信息并停止反应器。

    关于

    是否需要保持一串的Deferred呢?有时确实需要同时保持多个非同步任务,且并非同时完成。例如,可能需要建立一个端口扫描器运行testConnect函数来对应一个范围的端口。为了实现这个,使用DeferredList对象,作为例子2-5,来挂历众多的Deferred。

    代码
    from twisted.internet import reactor,defer
    from connectiontester import testConnect
    def handleAllResules(results,ports):
    for port,resultInfo in zip(ports,results):
    success,result
    =resultInfo
    if success:
    print 'Connected to port %i' % port
    reactor.stop()
    import sys
    host
    =sys.argv[1]
    ports
    =range(1,201)
    testers
    =[testConnect(host,port) for port in ports]
    defer.DeferredList(testers,consumeErrors
    =True).addCallback(handleAllResults,ports)
    reactor.run()

    运行portscan.py脚本并传递host参数。将会自动扫描1-200端口并报告结果。

    $ python portscan.py localhost

    Connected to port 22

    Connected to port 23

    Connected to port 25

    ... ...

    例子使用了Python的列表(list)的形式来创建一个列表存储由testConnect()返回的Deferred对象的列表。每一个testConnect()使用host参数,并测试1-200端口。

    testers=[testConnect(host,port) for port in ports]

    例子使用列表方式包装了Deferred对象成为DeferredList对象。DeferredList将会跟踪所有的Deferred对象的结果并传递作为首参数。当它们都完成了的时候,将会回调按照(success,result)的格式。在Deferred完成时,第一个回传值是TRue和第二个参数是Deferred传回结果。如果执行失败,则第一个参数是False且第二个参数是Failure对象包装的异常。consumeErrors键设置为True时告知DeferredList完成了吸收Deferred的所有错误。否则将会看到弹出的错误信息。

    当DeferredList完成时,results将会被传给handleAllResults函数,列表中的每个元素都会被扫描。handleAllResults使用了zip函数来匹配每个结果的端口。对每一个端口如果有成功的连接则打印信息。最终反应器会结束并结束程序。

    发送和接收数据

    一旦TCP连接被建立,就可以用于通讯。程序可以发送数据到另一台计算机,或者接收数据。

    使用Protocol的子类可以发送和接收数据。重载dataReceived方法用于控制数据的接收,仅在接收到数据时才被调用。使用self.transport.write来发送数据。

    例子2-6包括了DataForwardingProtocol的类,可以将接收到的数据写入self.output。这个实现创建了简单的应用程序,类似于netcat,将收到的数据传递到标准输出,用于打印接收的数据到标准输出。

    代码
    from twisted.internet import stdio,reactor,protocol
    from twisted.protocols import basic
    import re
    class DataForwardingProtocol(protocol.Protocol):
    def __init__(self):
    self.output
    =None
    self.normalizeNewLines
    =False
    def dataReceived(self,data):
    if self.normalizeNewLines:
    data
    =re.sub(r"(\r\n|\n)","\r\n",data)
    if self.output:
    self.output.write(data)
    class StdioProxyProtocol(DataForwardingProtocol):
    def connectionMade(self):
    inputForwarder
    =DataForwardingProtocol()
    inputForwarder.output
    =self.transport
    inputForwarer.normalizeNewLines
    =True
    stdioWarpper
    =stdio.StandardIO(inputForwarder)
    self.output
    =stdioWarpper
    print "Connected to server. Press Ctrl-C to close connection."
    class StdioProxyFactory(protocol.ClientFactory):
    protocol
    =StdioProxyProtocol
    def clientConnectionLost(self,transport,reason):
    reactor.stop()
    def clientConnectionFailed(self,transport,reason):
    print reason.getErrorMessage()
    reactor.stop()
    if __name__=='__main__':
    import sys
    if not len(sys.argv)==3:
    print "Usage: %s host port "__file__
    sys.exit(
    1)
    reactor.connectTCP(sys.argv[
    1],int(sys.argv[2]),StdioProxyFactory())
    reactor.run()

    运行dataforward.py并传入host和port两个参数。一旦连接就可以将所有来自服务器的消息送回服务器,所有接收的数据也同时显示在屏幕上。例如可以手动连接一个HTTP服务器,并发送HTTP请求到oreilly.com:

    $ python dataforward.py oreilly.com 80

    Connected to server.  Press Ctrl-C to close connection.

    HEAD / HTTP/1.0

    Host: oreilly.com

                              <--空行

    HTTP/1.1 200 OK

    ......

    它们是如何工作的?

    例子2-6开始于定义类DataForwardingProtocol。这个协议用于接受数据并存入self.output,这个属性可以用write方法访问,如同self.output.write。DataForwardingOutputProtocol包含一个叫做normalizeNewLines的属性。如果这个属性设置为True,将会由Unix风格的\n换行改变为\r\n这种常见的网络换行方式。

    作为DataForwardingProtocol的子类StdioProxyProtocol类具体负责工作。一旦连接被建立,将会创建一个叫做inputForwarder的DataForwardingProtocol实例,并设置输出为self.transport。然后包装twisted.internet.stdio.StandardIO的实例inputForwarder,以标准IO的方式代替网络连接。这一步对所有的通过StdioProxyProtocol方式的网络连接都有效。最终设置StdioProxyProtocol的output属性到值stdioWarpper,所以数据的接收工作定义到了标准输出。

    协议定义之后,很容易定义StdioProxyFactory,将其protocol属性设置为StdioProxyProtocol,并且处理反应器的停止和连接、失败工作。调用reactor.connectTCP连接,然后依靠reactor.run()来控制事件循环。

    2.4 接受客户端的连接

    前面的实验都是讲解如何作为客户端进行连接。Twisted也同样适合于编写网络服务器,用于等待客户端连接。下面的实验将会展示如何编写Twisted服务器来接受客户端连接。

    2.4.1 下面如何做?

    创建一个Protocol对象来定义服务器行为。创建一个ServerFactory对象并引用Protocol,并传递给reactor.listenTCP。例子2-7展示了简单的echo服务器,简单的返回客户端发来的信息。

    代码
    from twisted.internet import reactor,protocol
    from twisted.protocols import baisc
    class EchoProtocol(basic.LineReceiver):
    def lineReceived(self,line):
    if line=='quit':
    self.sendLine(
    "Goodbye.")
    self.transport.loseConnection()
    else:
    self.sendLine(
    "You said: "+line)
    class EchoServerFactory(protocol.ServerFactory):
    protocol
    =EchoProtocol
    if __name__=="__main__":
    port
    =5001
    reactor.listenTCP(port,EchoServerFactory())
    reactor.run()

    当这个例子运行时,将会在5001端口监听,并报告已经建立的连接。

    $python echoserver.py

    Server running, Press Ctrl-C to stop.

    Connection from  127.0.0.1

    Connection from  127.0.0.1

    在另一个终端,使用netcat、telnet或者dataforward.py(例子2-6)来连接服务器。将会返回键入的例子。输入quit来关闭连接。

    $ python dataforward.py localhost 5001

    Connected to server.  Press Ctrl-C to close connection.

    hello

    You said: hello

    twisted is fun

    You said: twisted is fun

    quit

    Goodbye.

    它们是如何工作的?

    Twisted服务器使用相同的Protocol类作为客户端。为了复用,EchoProtocol继承了twisted.protocols.basic.LineReceiver,作为Protocol.LineReceiver的轻量级实现,可以自动按照获得的行来产生处理。当EchoProtocol接收到一个行时,就会返回收到的行,除非遇到了'quit'将退出。

    下一步,定义了EchoServerFactory类。EchoServerFactory继承自ServerFactory,作为ClientFactory的服务器端,并设置了EchoProtocol为protocol属性。EchoServerFactory的实例作为第二个参数传递给reactor.listenTCP,第一个参数是端口号。

  • 相关阅读:
    php反射类 ReflectionClass
    大写中文数字-財务
    Cookie/Session机制具体解释
    具体解释VB中连接access数据库的几种方法
    Hibernate Criterion
    hdu1151 Air Raid,DAG图的最小路径覆盖
    【收藏】十大Webserver漏洞扫描工具
    美国地名大全(美国城市名称英文、中文)
    图像切割之(五)活动轮廓模型之Snake模型简单介绍
    数据库索引的作用和长处缺点
  • 原文地址:https://www.cnblogs.com/sevenyuan/p/1884420.html
Copyright © 2011-2022 走看看