Twisted是一个基于Reactor模式的异步IO网络框架
Reactor模式就是利用循环体来等待事件发生,然后处理发生的事件的模式,如上图所示。
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 就能收到数据 进而处理粘包问题了
服务器收到需要的数据了。。。。
到此流程走完了。。。