Twisted框架中reactor事件分析

Twisted是一个基于Reactor模式的异步IO网络框架

Reactor模式就是利用循环体来等待事件发生,然后处理发生的事件的模式,如上图所示。

Twisted基础介绍(一) - 知乎

1:import导入reactor  调用run 和stop 

from twisted.internet import reactor
reactor.run()
reactor.stop()

2:import 导入reactor的时候 会删除modules里边已经存在的reactor

#twisted\internet\reactor.py
from __future__ import division, absolute_import

import sys
del sys.modules['twisted.internet.reactor']
from twisted.internet import default
default.install()

3:install安装reactor  根据platform 来安装对应的reactor 以epollreactor为例

#twisted\internet\default.py
def _getInstallFunction(platform):
    try:
        if platform.isLinux():
            try:
                from twisted.internet.epollreactor import install
            except ImportError:
                from twisted.internet.pollreactor import install
        elif platform.getType() == 'posix' and not platform.isMacOSX():
            from twisted.internet.pollreactor import install
        else:
            from twisted.internet.selectreactor import install
    except ImportError:
        from twisted.internet.selectreactor import install
    return install

install = _getInstallFunction(platform)
#twisted\internet\epollreactor.py
def install():
    """
    Install the epoll() reactor.
    """
    p = EPollReactor()
    from twisted.internet.main import installReactor
    installReactor(p)
#twisted\internet\main.py
def installReactor(reactor):
    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

到此 reactor 根据平台不同 安装对应的reactor,reactor安装好之后 就可以正常的使用了

4:调用reactor.listenTCP()  listenSSL()启动TCP服务器

      调用reactor.connectTCP()  connectSSL()  连接TCP服务器

      同时也可以支持 UDP相关的,比如reactor.listenUDP()

这些API是 PosixReactorBase类的,EPollReactor、SelectReactor、PollReactor都是继承于它,所以reactor都是可以调用的。。。

#twisted\internet\posixbase.py
def listenTCP(self, port, factory, backlog=50, interface=''):
        p = tcp.Port(port, factory, backlog, interface, self)
        p.startListening()
        return p

startListening() 主要是 createsocket + bind + listen,就是一个server启动的常规流程

#twisted\internet\tcp.py
def startListening(self):
        """Create and bind my socket, and begin listening on it.

        This is called on unserialization, and must be called after creating a
        server to begin listening on the specified port.
        """
        _reservedFD.reserve()
        if self._preexistingSocket is None:
            # Create a new socket and make it listen
            try:
                skt = self.createInternetSocket()
                if self.addressFamily == socket.AF_INET6:
                    addr = _resolveIPv6(self.interface, self.port)
                else:
                    addr = (self.interface, self.port)
                skt.bind(addr)
            except socket.error as le:
                raise CannotListenError(self.interface, self.port, le)
            skt.listen(self.backlog)
        else:
            # Re-use the externally specified socket
            skt = self._preexistingSocket
            self._preexistingSocket = None
            # Avoid shutting it down at the end.
            self._shouldShutdown = False

        # Make sure that if we listened on port 0, we update that to
        # reflect what the OS actually assigned us.
        self._realPortNumber = skt.getsockname()[1]

        log.msg("%s starting on %s" % (
                self._getLogPrefix(self.factory), self._realPortNumber))

        # The order of the next 5 lines is kind of bizarre.  If no one
        # can explain it, perhaps we should re-arrange them.
        self.factory.doStart()
        self.connected = True
        self.socket = skt
        self.fileno = self.socket.fileno
        self.numberAccepts = 100

        self.startReading()
#twisted\internet\tcp.py
def createInternetSocket(self):
        s = base.BasePort.createInternetSocket(self)
        if platformType == "posix" and sys.platform != "cygwin":
            s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        return s

#twisted\internet\base.py
def createInternetSocket(self):
        s = socket.socket(self.addressFamily, self.socketType)
        s.setblocking(0)
        fdesc._setCloseOnExec(s.fileno())
        return s

startListening()  顺带干的事儿  self.factory.doStart() 以及self.startReading()

doStart()会打印一行日志提示

#twisted\internet\protocol.py
def doStart(self):
        """
        Make sure startFactory is called.

        Users should not call this function themselves!
        """
        if not self.numPorts:
            if self.noisy:
                _loggerFor(self).info("Starting factory {factory!r}",
                                      factory=self)
            self.startFactory()
        self.numPorts = self.numPorts + 1

这也就验证了每次启动服务器的时候 就会打印如下类似的日志

startReading()会把当前的对象注册到reactor的reader事件表中,坐等client来连接时好处理事件

def startReading(self):
        """Start waiting for read availability.
        """
        self.reactor.addReader(self)

来看看epollreactor的addReader()

5:reactor.run()流程

继承关系:EPollReactor--->posixbase.PosixReactorBase---->_SignalReactorMixin

class EPollReactor(posixbase.PosixReactorBase, posixbase._PollLikeMixin):
    pass
class PosixReactorBase(_SignalReactorMixin, _DisconnectSelectableMixin,ReactorBase):
    pass

 reactor调用的run 其实是基类 _SignalReactorMixin 中的run()方法

#twisted\internet\base.py
def run(self, installSignalHandlers=True):
    self.startRunning(installSignalHandlers=installSignalHandlers)
    self.mainLoop()

def startRunning(self, installSignalHandlers=True):
    self._installSignalHandlers = installSignalHandlers
    ReactorBase.startRunning(self)

def mainLoop(self):
        while self._started:
            try:
                while self._started:
                    # Advance simulation time in delayed event
                    # processors.
                    self.runUntilCurrent()
                    t2 = self.timeout()
                    t = self.running and t2
                    self.doIteration(t)
            except:
                log.msg("Unexpected error in main loop.")
                log.err()
            else:
                log.msg('Main loop terminated.')
            

run里边 就干了2件事儿 

5.1:第一件事儿就是self._installSignalHandlers赋值为True,以便调用 _handleSignals

def _handleSignals(self):
        """
        Install the signal handlers for the Twisted event loop.
        """
        try:
            import signal
        except ImportError:
            log.msg("Warning: signal module unavailable -- "
                    "not installing signal handlers.")
            return

        if signal.getsignal(signal.SIGINT) == signal.default_int_handler:
            # only handle if there isn't already a handler, e.g. for Pdb.
            signal.signal(signal.SIGINT, self.sigInt)
        signal.signal(signal.SIGTERM, self.sigTerm)

        # Catch Ctrl-Break in windows
        if hasattr(signal, "SIGBREAK"):
            signal.signal(signal.SIGBREAK, self.sigBreak)

这些信号还是很重要的,最常用的就是ctrl + C 退出reactor 退出进程等等

5.2:第二件事儿就是 启动mainLoop() loop里边就是个while死循环,执行runUntilCurrent和doIteration,runUntilCurrent方法主要做了两件事分别是把threadCallQueue和pendingTimedCalls 里的对象执行一遍。重点关注doIteration
doIteration:select,poll,epoll实现各有不同

#twisted\internet\epollreactor.py
def doPoll(self, timeout):
        if timeout is None:
            timeout = -1  # Wait indefinitely.

        try:
            
            l = self._poller.poll(timeout, len(self._selectables))
        except IOError as err:
            if err.errno == errno.EINTR:
                return
            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)

    doIteration = doPoll

doIteration里边处理的其实就是 用IO多路复用来处理 连接事件了

doPoll中调用了父类_PollLikeMixin中的_doReadOrWrite, _doReadOrWrite根据event类型 会调用doRead() 或者 doWrite(),现在以doRead()为例

 #twisted\internet\tcp.py

上层应用层 写dataReceived 就能收到数据 进而处理粘包问题了

 

 服务器收到需要的数据了。。。。

到此流程走完了。。。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值