• Twisted网络编程入门


    Twisted是用Python实现的基于事件驱动的网络引擎框架,功能非常丰富,基本包括了常用的网络组件。

    所谓事件驱动,就是说程序就像是一个报警器(reactor),时刻等待着外部事件(event),诸如有人入侵等,一旦有事件发生,程序就会触发一些特定的操作(callback),注入拨打报警电话等。

    Reactor

    reactor是twisted框架中的核心,负责管理各种回调函数,接收各种系统事件,并根据规则对事件和回调函数进行匹配、派发处理。在Linux系统中,由于秉承“一切皆文件”的理念,系统中的事件主要就是文件事件以及定时事件。其实大多使用了不同系统中特定的API,具体来说,Linux上默认是epoll,OS X默认是Poll,其他系统默认都是select,每个API都会有一个特定的reactor与之对应。

    从代码上来看,每当你import twisted.internet.reactor时,他就会根据当前系统返回特定的reactor module(twisted/twisted/internet/default.py),然后调用特定reactor module的install()函数。这里我们看一下对应Linux系统的epollreactor的install函数

    # twisted/twisted/internet/epollreactor.py
    
    def install():
        """
        Install the epoll() reactor.
        """
        p = EPollReactor()
        from twisted.internet.main import installReactor
        installReactor(p)
    
    #twisted/twisted/intenet/main.py
    
    def installReactor(reactor):
        """
        Install reactor C{reactor}.
    
        @param reactor: An object that provides one or more IReactor* interfaces.
        """
        # this stuff should be common to all reactors.
        import twisted.internet
        import sys
        if 'twisted.internet.reactor' in sys.modules:
            raise error.ReactorAlreadyInstalledError("reactor already installed")
        twisted.internet.reactor = reactor
        sys.modules['twisted.internet.reactor'] = reactor
    

    代码是非常清楚的,就是创建了一个EPollReactor的实例,然后使用sys.module[""] = reactor将其注册为默认的reactor,这也就是使用twisted编程时不需显示创建reactor的原因。

    究竟一个reactor到底干了些什么呢?twisted在这一块为了抽象以及扩展性,写了大量的类,用了大量的继承,但是简单来说,最终会落回到EPollReactor的doPoll函数中。

    def doPoll(self, timeout):
            """
            Poll the poller for new events.
            """
            if timeout is None:
                timeout = -1  # Wait indefinitely.
    
            try:
                # Limit the number of events to the number of io objects we're
                # currently tracking (because that's maybe a good heuristic) and
                # the amount of time we block to the value specified by our
                # caller.
                l = self._poller.poll(timeout, len(self._selectables))
            except IOError as err:
                if err.errno == errno.EINTR:
                    return
                # See epoll_wait(2) for documentation on the other conditions
                # under which this can fail.  They can only be due to a serious
                # programming error on our part, so let's just announce them
                # loudly.
                raise
    
            _drdw = self._doReadOrWrite
            for fd, event in l:
                try:
                    selectable = self._selectables[fd]
                except KeyError:
                    pass
                else:
                    log.callWithLogger(selectable, _drdw, selectable, fd, event)
    

      其中的self._poller通过epoll就是一个epoll调用的返回值,简单点说,也就是一个典型的epoll应用场景,在timeout时间内,检查需要处理的事件,然后通过log.callWithLogger调用相关的回调函数。

    Transport

    用于抽象通信双方之间的链接(TCP、UDP等),通过它可以向对方发送数据,获取对方信息等。

    Protocol

    用于抽象双方的通信协议,包括如何建立链接、处理数据以及出错处理等接口。一个类实现了这些接口,就等于定义了一个新的协议,twisted自带的协议非常多,一般情况下不需要自定义协议,除非你有特殊要求。

    Protocol Factory

    熟悉设计模式的应该对factory不陌生,简单来说,这个类定义了一个接口buildProtocol,用于返回一个Protocl实例(但是通常的做法是在factory类中使用类成员protocol来注册响应的Protocol),同时在这个类里你可以保存一些数据,用于配置或者管理系统中所有的Protocol实例。

    简单的Echo服务器程序

    # Server
    from twisted.internet import protocol, reactor
    
    class Echo(protocol.Protocol):
        def dataReceived(self, data):
            self.transport.write(data)
    
    class EchoFactory(protocol.Factory):
        def buildProtocol(self, addr):
            return Echo()
    
    reactor.listenTCP(8080, EchoFactory())
    reactor.run()
    
    # Client
    from twisted.internet import protocol, reactor
    
    class EchoClient(protocol.Protocol):
        def connectionMade(self):
            self.transport.write("Hello, world!")
    
        def dataReceived(self, data):
            self.transport.loseConnection()
    
    class EchoFactory(protocol.ClientFactory):
        def buildProtocol(self, addr):
            return EchoClient()
    
        def clientConnectionFailed(self, connector, reason):
            reactor.stop()
    
        def clientConnectionLost(self, connector, reason):
            reactor.stop()
    
    reactor.connectTCP("localhost", 8080, EchoFactory())
    reactor.run()
    

      

      

  • 相关阅读:
    jQuery的基本使用、实践、效果、API
    关于Nginx那些事儿
    Linux下安装Nginx(保姆教程)
    jQuery的那些事儿
    k8s的应用回滚--record
    MySQL之PXC
    MySQL之高可用MHA
    MySQL之主从半同步复制
    MySQL之MyCat
    MySQL之主从复制
  • 原文地址:https://www.cnblogs.com/jimmychange/p/3508041.html
Copyright © 2020-2023  润新知