Python中reactor,factory,protocol
最为简单的情况下,除了了解清reactor的简单使用,你还要了解Protocol和Factory。它们最终都会由reactor的侦听建立和run来统一调度起来。
建立服务器的第一个要解决的问题就是服务与外界的交流协议。协议的定义在twisted中是通过继承twisted.internet.protocol.Protocol类来实现的。在协议中你可以定义连接、数据接收、断开连接等一系列的事件如果进行响应。但是对于所有连接上来的协议的建立、持久配置数据的存储这样的内容并不会保存在协议中。
持久配置数据的存储通常都会保存在工厂里。
工厂的定义在twisted中是通过继承twisted.internet.protocol.Factory类来实现的。twisted提供了缺省的工厂实现最为普通的需求。它会实例化每个协议,并且通过设置每个协议中factory属性来使协议可以使用到它自己,做这个设置的作用就是让协议在进行处理连接数据时可以使用到工厂中存储的持久配置数据。工厂的启动是需要reactor真实的建立侦听并启动才可以实现的。
reactor侦听的建立可以参考 twisted.internet.interfaces.IReactorTCP.listenTCP。这时我们先只说建立TCP服务器的侦听,如果你需要其它种类的侦听建立参考IReactor*.listen*系列API。
总结一下,我们书写一个twisted的Daemon,实质上会关注三个层次的对象。它们互相可配置,可独立开发,只需要通过简单的调用配置就可结合使用。第一层次就是侦听的建立、工厂的初始化、服务器的运行,它需要reactor的开发。第二个层次就是服务的初始化、用户连接的建立、持久配置数据的存储、协议的实例化,它需要factory的开发。第三个层次就是用户连接建立后的事件处理,这就需要protocol的开发了。
一、
protocol:内部实现的主要是连接在通信时的动作;
内部有transport,可进行write(), getpeer()(host,port)
还有factory
Factory:保存的是连接方的信息,当有链接过来时,factory会初始化一个protocol与对方建立连接,所以factory中有protocol. 因为protocol的方法经常会用到factory里的属性变量,所以protocol类中也有factory。这里就有一个循环引用的问题,Python中,有些类是有垃圾清理机制的,但是对于那些有定义方法__del__()的类,就会有内存泄露的问题。
Reactor:是管理twisted框架的核心。所有的事件都会触发reactor,然后他会开启服务,初始化factory,factory再初始化protocol。
Factory和protocol的基础实现在protocol.py文件中。
二、
Reactor:
是twisted框架中很重要的概念,事件驱动。他负责监测所有事件。
最常用的是run(), stop(), callLater()
callLater()实际是实例化了一个DelayedCall()类,并将实例的句柄返回。可进行cancel(), reset(), delay()等操作。
Reactor的实现,应该继承了internet\base.py文件中的类ReactorBase(object)还有类_SignalReactorMixin,其中,_SignalReactorMixin类中有定义run()函数。
ReactorBase(object)类中,有方法stop()。好多reactor和thread的操作函数也能在这里面找到,上面的callLater()就是在这里实现的。
在internet\posixbase.py文件中,有类PosixReactorBase(_SignalReactorMixin, ReactorBase),是上面说到的两个类的继承。在这里,实现了各种listen**函数,主要服务员server;还有对应的connect**函数,针对client。
<一>、例如:客户端可以使用:reactor. connectTCP(self, host, port, factory, timeout=30, bindAddress=None)
这是一个直接可以用的,所以reactor必定继承自PosixReactorBase。
在connectTCP()中,实现如下:
c = tcp.Connector(host, port, factory, timeout, bindAddress, self)
c.connect()
return c
第一句:实例一个tcp.Connector类,该类继承自base. BaseConnector;
类base. BaseConnector实现的方法有:disconnect(), connect(),stopConnecting(self), cancelTimeout(self), buildProtocol(self, addr), connectionFailed(self, reason), connectionLost(self, reason)…
这些方法,和factory中的方法,函数名很相似,其实这些函数的内部就是调用相应的factory方法的。该类中有个很重要的变量就是self.factory。
第二句:是连接的发起函数,主要是开启了factory(connectTCP函数中传入的)。
Connect()定义如下:
def connect(self):
"""Start connection to remote server."""
if self.state != "disconnected":
raise RuntimeError, "can't connect in this state"
self.state = "connecting"
if not self.factoryStarted:
self.factory.doStart()
self.factoryStarted = 1
self.transport = transport = self._makeTransport()
if self.timeout is not None:
self.timeoutID = self.reactor.callLater(self.timeout, transport.failIfNotConnected, error.TimeoutError())
self.factory.startedConnecting(self)
进过上面分析,reactor, factory, protocol就联系到一起了,很多操作也就清晰了。再回过去读最前面引用的内容,应该好理解多了。
<二>、对于服务端:reactor. listenTCP(self, port, factory, backlog=50, interface='')
具体实现如下:
p = tcp.Port(port, factory, backlog, interface, self)
p.startListening()
return p
第一句:实例一个tcp.Port类,该类继承了base.BasePort, _SocketCloser。
实现的方法有:
createInternetSocket(self),startListening(self),_buildAddr(self, (host, port)),
doRead(self),connectionLost(self, reason),getHost(self),doWrite(self)
loseConnection(self, connDone=failure.Failure(main.CONNECTION_DONE))
也有self.factory变量
第二句:做必要的准备,如:create and bind socket,侦听端口,读取数据等。
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.
"""
try:
skt = self.createInternetSocket()
skt.bind((self.interface, self.port))
except socket.error, le:
raise CannotListenError, (self.interface, self.port, le)
# 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.factory.__class__, self._realPortNumber))
# The order of the next 6 lines is kind of bizarre. If no one
# can explain it, perhaps we should re-arrange them.
self.factory.doStart()
skt.listen(self.backlog)
self.connected = True
self.socket = skt
self.fileno = self.socket.fileno
self.numberAccepts = 100
self.startReading()
其中skt是socket.socket()返回的socket句柄。
三、继续深入:
上面讲到的是reactor.connectTCP(),这个方法会直接开始工作的(初始化factory,protocol等),也许我们需要自己手工控制这个过程。下面是利用类去实现:
\appliction\internet.py文件中的,类TCPClient,当时为了找到这个类名,花了很多时间,这个类名是通过组合而成。
其原型是:class _AbstractClient(_VolatileDataService)
还有class _AbstractServer(_VolatileDataService)作为服务端的原型
一下几个类均是上面两个类的一个模式:
TCPServer, TCPClient,
UNIXServer, UNIXClient,
SSLServer, SSLClient,
UDPServer, UDPClient,
UNIXDatagramServer, UNIXDatagramClient,
MulticastServer
class _AbstractClient和class _AbstractServer最终都是从\appliction\server.py中Server继承而来的。两个类都是服务,只是在实现过程稍有差别,一个针对connect,一个针对port。
它们均有:
self.reactor,是通过参数传递进去的。
Self.method,保存的是连接的方式,如tcp,udp
self._connection,保存连接的(其实是上面的listen**或connect**的返回值)
已经实例了对象,如何开始服务(工作)呢? startService(self)
1)、先来看看class _AbstractServer中的:
def startService(self):
service.Service.startService(self)
if self._port is None:
self._port = self._getPort()
def _getPort(self):
"""
Wrapper around the appropriate listen method of the reactor.
@return: the port object returned by the listen method.
@rtype: an object providing L{IListeningPort}.
"""
if self.reactor is None:
from twisted.internet import reactor
else:
eactor = self.reactor
return getattr(reactor, 'listen%s' % (self.method,))(
*self.args, **self.kwargs)
Getattr()这个函数不错,呵呵
最终调用的还是reactor中的linsten**,但是经过是先定义一个server,再通过server.startServer()开启的。
2)、再来看看class _AbstractClient
def startService(self):
service.Service.startService(self)
self._connection = self._getConnection()
def _getConnection(self):
"""
Wrapper around the appropriate connect method of the reactor.
@return: the port object returned by the connect method.
@rtype: an object providing L{IConnector}.
"""
if self.reactor is None:
from twisted.internet import reactor
else:
reactor = self.reactor
return getattr(reactor, 'connect%s' % (self.method,))(
*self.args, **self.kwargs)
参照上面的,很清晰了。
Python中reactor,factory,protocol的更多相关文章
- python中的generator(coroutine)浅析和应用
背景知识: 在Python中一个function要运行起来,它在python VM中需要三个东西. PyCodeObject,这个保存了函数的代码 PyFunctionObject,这个代表一个虚拟机 ...
- python中的 descriptor
学好和用好python, descriptor是必须跨越过去的一个点,现在虽然Python书籍花样百出,但是似乎都是在介绍一些Python库而已,对Python语言本身的关注很少,或者即使关注了,但是 ...
- 在python中处理XML
XML是实现不同语言或程序之间进行数据交换的协议,XML文件格式如下: <data> <country name="Liechtenstein"> < ...
- Python中的内置函数
2.1 Built-in Functions The Python interpreter has a number of functions built into it that are alway ...
- Python 中的 TK编程
可爱的 Python:Python 中的 TK编程 http://www.ibm.com/developerworks/cn/linux/sdk/python/charm-12/ python che ...
- python中那些双下划线开头得函数和变量--转载
Python中下划线---完全解读 Python 用下划线作为变量前缀和后缀指定特殊变量 _xxx 不能用'from module import *'导入 __xxx__ 系统定义名字 __x ...
- Python中使用ElementTree解析xml
在Python中,ElementTree是我们常用的一个解析XML的模块 1.导入ElementTree模块 from xml.etree import ElementTree as ET 2.初始化 ...
- 可爱的 Python : Python中的函数式编程,第三部分
英文原文:Charming Python: Functional programming in Python, Part 3,翻译:开源中国 摘要: 作者David Mertz在其文章<可爱的 ...
- 操作系统底层原理与Python中socket解读
目录 操作系统底层原理 网络通信原理 网络基础架构 局域网与交换机/网络常见术语 OSI七层协议 TCP/IP五层模型讲解 Python中Socket模块解读 TCP协议和UDP协议 操作系统底层原理 ...
随机推荐
- Unquotted string '"2016-07-19"'
自己挖的坑,含泪跳进去也要填平. ---题记 1.问题: a. 在前端使用JSON.stringify(json)转化数组对象为字符串,然后传给后台: var dateArray = new Ar ...
- RMAN的实战篇--备份脚本
案列一. 目标: 1.每天夜间1 点执行:2.数据库全备,同时备份控制文件及归档日志文件,备份文件保存至: /backup\目录下,并在完成归档日志文件备份后,自动删除已备份的归档日志:3.备份保留7 ...
- linux进程编程:子进程创建及执行函数简介
linux进程编程:子进程创建及执行函数简介 子进程创建及执行函数有三个: (1)fork();(2)exec();(3)system(); 下面分别做详细介绍.(1)fork() 函数定 ...
- SG函数 专题练习
[hdu1536][poj2960]S-Nim 题意 题意就是给出一个数组h,为每次可以取石子的数目. 然后给你n堆石子每堆si.求解先手能不能赢? 分析 根据\(h\)数组预处理出\(sg[i]\) ...
- while 和 for 对比
for 语句实例 本例中的循环使用 for 循环来显示 cars 数组中的所有值: cars=["BMW","Volvo","Saab",& ...
- guava学习--File
使用Files类来执行那些基本的任务,比如:移动或复制文件,或读取文件内容到一个字符串集合 Closer类,提供了一种非常干净的方式,确保Closeable实例被正确的关闭 ByteSource 和 ...
- codeforces 451E Devu and Flowers
题意:有n个瓶子每个瓶子有 f[i] 支相同的颜色的花(不同瓶子颜色不同,相同瓶子花视为相同) 问要取出s支花有多少种不同方案. 思路: 如果每个瓶子的花有无穷多.那么这个问题可以转化为 s支花分到 ...
- Codeforces Round #370 (Div. 2) E. Memory and Casinos 线段树
E. Memory and Casinos 题目连接: http://codeforces.com/contest/712/problem/E Description There are n casi ...
- IT公司100题-27-跳台阶问题
问题描述: 一个台阶总共有n阶,一次可以跳1级或者2级.求总共有多少种跳法. 分析: 用f(n)表示n阶台阶总共有多少种跳法.n阶台阶,第一可以选择跳1阶或者2阶,则f(n) = f(n-1) + ...
- 使用excel快速制表 拒绝粗心
办公室打印个表格 使用了word打印后 发现 id重复很多 只好网上找了点excel 2003资料 学习小 快速制作表格 新建一个excel文件. 在新建excel中,用鼠标选中需要的表格行数列数,然 ...