Twisted 综述
Twisted 框架概况
Twisted 是一个有着10多年历史的开源事件驱动框架。Twisted 支持很多协议,包括传输层的TCP、UDP、TLS,以及应用层的HTTP、FTP等。对所有这些协议,Twisted提供了
客户端和服务器方面的开发工具。
Twisted 是一个高性能的编程框架。在不同的操作系统平台上,Twisted 利用不同的底层技术实现了高效能通信。在 Windows 中,Twisted 的实现基于 I/O 完成端口(IOCP,Input/Output Completion Port) 技术,它保证了底层高效地将 I/O 事件通知给框架及应用程序。在 Linux 中,Twisted 的实现基于 epoll 技术, epoll 是 Linux 下多路复用 I/O 接口 select/poll 的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。
在开发技术上,Twisted 引导程序员使用异步编程模型。Twisted 提供了丰富的 Defer、Threading等特性来支持异步编程。
在 Linux 与 macOS 中 安装 Twisted
Twisted在安装过程中需要先在设备上编译,因此在安装之前需要确保安装了Python编译开发包。该步骤在不同的操作系统略有不同,以Ubuntu Linux 举例:
# apt-get install python3-dev // 安装 Python3 开发包
# pip install twisted // 安装 Twisted
而在macOS系统的brew安装工具中自带了Python开发包,因此可以直接使用上述第二条命令(pip)安装。
安装完成后可以用如下命令查看Twisted版本:
# pip freeze | grep Twisted
Twisted==19.2.1
举个例子: 开发TCP广播系统
该广播系统接受任意客户端的链接请求,并且将任意客户端发给服务器的消息转发给所有其他客户端。本系统是一个基本的实时通信模型。
使用 Twisted 进行基于传输层TCP的编程时,无须程序员操作Socket 的 bind、send、receive等基本原语;而是直接针对Twisted 的 Protocol、Factory 等类进行编程,定义它们的子类并重写connectionMade、dataReceived进行事件化的TCP编程风格。
1、开发 Protocol 子类
针对每个客户端连接,Twisted 框架建立了一个Protocol子类的实例管理该连接。开发者需要编写该子类,使其能够处理3个基本事件响应函数。
- connectionMade(): 当连接建立时由 Twisted 框架调用。在实际应用中,本函数的主要作用常常是在系统中注册该连接,方便以后使用。
- dataReceived(): 当收到客户端的数据时由 Twisted 框架调用。
- connectionLost(): 当连接断开时由 Twisted 框架调用。在实际应用中,本函数常常用来清理连接占用的资源。
from twisted.internet.protocol import Protocol
import random
import string clients = [] class Spreader(Protocol): def __init__(self, factory):
self.factory = factory def connectionMade(self):
self.factory.numPortocols += 1
self.client_id = ''.join(random.sample(string.ascii_letters + string.digits, 8)).lower()
print("new connect: %d" % self.factory.numPortocols)
self.transport.write(
(u"欢迎来到 Twisted World, 您是第 %d 个客户端用户!\n" % self.factory.numPortocols).encode()) clients.append(self) def connectionLost(self, reason):
clients.remove(self)
print("lost connect: %s" % self.client_id) def dataReceived(self, data):
if data == "close":
self.transport.loseConnection()
print("%s closed " % self.client_id)
else:
print("spreading message from %s % s" % (self.client_id, data))
for client in clients:
if client != self:
client.transport.write(data)
代码解析如下:
- 用全局列表变量 clients 保存所有的客户端的连接 (即 Protocol 子类 Spreader 的实例)。
- 定义 Protocol 的子类 Spreader, 在其中实现需要重写的方法。
- 在 connectionMade() 中对连接的客户端进行计数,并将 self 保存到 clients 列表中。
- 在 connectionLost() 中执行与 connectionMade() 函数相反的操作。
- 在 dataReceived() 中轮询当前 clients 列表中的所有客户端,将收到的数据通过 Protocol.transport.write() 函数分发给除自己之外的所有客户端。
- 如果收到客户端发来的数据 “close",则调用 Protocol.transport.loseConnection() 主动关闭与客户端的连接。
2、开发 Factory 子类
Twisted 中的 Factory 子类起到对 Protocol类的管理作用,当有新的客户端连接时,框架调用 Factory.buildProtocol(),使得程序员可以在这里创建 Protocol 子类的实例。Factory 子类及服务启动程序的代码如下:
from twisted.internet.protocol import Factory
from twisted.internet.endpoints import TCP4ServerEndpoint
from twisted.internet import reactor class SpreadFactory(Factory): def __init__(self):
self.numPortocols = 0 def buildProtocol(self, addr):
return Spreader(self) if __name__ == "__main__": # 8006 是本服务器的监听端口, 建议选择大于1024 的端口
endpoint = TCP4ServerEndpoint(reactor, 8006)
endpoint.listen(SpreadFactory())
# 挂起运行
reactor.run()
建立 Factory 的子类 SpreadFactory,在其中只需要重写两个函数:在 __init__ 中将客户端计数器 self.numProtocols 置 0;在buildProtocol() 中建立 Protocol 子类 Spreader 的实例。
通过 TCP4ServerEndpoint() 定义服务器的监听端口,并用 listen() 函数指定该端口所绑定的 Factory 子类实例,运行 twisted.internet.reactor.run() 可启动服务器。
3、广播客户端
Twisted 同样提供了基于 Protocol 类的 TCP 客户端的编程方法。实现一个与服务器程序相匹配的 TCP 客户端程序。
from twisted.internet.protocol import Protocol, ClientFactory
from twisted.internet import reactor
import sys
from datetime import datetime class Echo(Protocol):
def connectionMade(self):
print("Connected to the server!") def dataReceived(self, data):
print("got messages: ", data.decode())
reactor.callLater(5, self.say_hello) def connectionLost(self, reason):
print("Disconnected from the server!") def say_hello(self):
if self.transport.connected:
self.transport.write((u"hello, I'm %s %s" % (sys.argv[1], datetime.now())).encode()) class EchoClientFactory(ClientFactory): def __init__(self):
self.protocol = None def startedConnecting(self, connector):
print("started to connect.") def buildProtocol(self, addr):
self.protocol = Echo()
return self.protocol def clientConnectionLost(self, connector, reason):
print("Lost connection. Reason:", reason) def clientConnectionFailed(self, connector, reason):
print("Connection failed. Reason:", reason) if __name__ == "__main__":
host = "127.0.0.1"
port = 8006
factory = EchoClientFactory()
reactor.connectTCP(host, port, factory)
reactor.run()
解析如下:
- 与服务端类似,使用 Protocol 管理连接,其中可重载的函数 connectionMade()、dataReceived()、connectionLost() 等含义与服务器中含义相同。
- 定义 ClientFactory 的子类 EchoClientFactory, 用于构造 Protocol 子类 Echo。ClientFactory 继承自 Factory 类,这里重写了它的3个事件响应函数,即 startedConnection() 函数在连接建立时被调用;clientConnectionLost() 函数在连接断开时被调用;clientConnectionFailed() 函数在连接建立失败时被调用。
- 在 Echo.dataReceived() 函数中,每次接收到消息后用 reactor.callLater() 函数延迟调用 say_hello() 函数。
- 在 say_hello() 函数中使用 self.transport.connected 属性判断当前是否处于连接状态。如果是则调用 self.transport.write() 函数向服务器发送消息。
- twisted.internet.reactor.connectTCP() 函数用于指定要连接的服务器地址和端口,然后仍然要调用 twisted.internet.reactor.run() 函数启动事件循环。
为了更好地观察本例中 Echo(Protocol 的子类)与 EchoClientFactory( ClientFactory 的子类) 两个类之间回调事件函数的执行顺序,现在打开三个命令行控制台,分别执行一个服务器程序和两个客户端程序,比如:
# python server.py // 服务器程序
# python client.py Alice // 客户端程序
# python client.py Bob // 客户端程序
运行若干秒后用 CTRL-C 终止服务器程序的执行。观察服务器程序:
new connect: 1
spreading message from zjqaohpd b"hello, I'm Alice 2019-06-18 21:02:57.807600"
new connect: 2
spreading message from v0f4fhl3 b"hello, I'm Bob 2019-06-18 21:03:13.586417"
spreading message from zjqaohpd b"hello, I'm Alice 2019-06-18 21:03:18.593345"
spreading message from v0f4fhl3 b"hello, I'm Bob 2019-06-18 21:03:23.600063"
spreading message from zjqaohpd b"hello, I'm Alice 2019-06-18 21:03:28.604293"
lost connect: v0f4fhl3
lost connect: zjqaohpd
观察 Alice 客户端:
started to connect.
Connected to the server!
got messages: 欢迎来到 Twisted World, 您是第 1 个客户端用户! got messages: hello, I'm Bob 2019-06-18 21:03:13.586417
got messages: hello, I'm Bob 2019-06-18 21:03:23.600063
Disconnected from the server!
Lost connection. Reason: [Failure instance: Traceback (failure with no frames):
<class 'twisted.internet.error.ConnectionDone'>: Connection was closed cleanly.
]
结合服务器程序、客户端程序中的代码,在连接建立与关闭时回调事件函数的执行顺序如下:
- 建立连接
- ClientFactory.startedConnecting()
- Protocol.connectionMade()
- 已连接
- 用 Protocol.dataReceived() 接收消息;
- 用 Protocol.transport.write() 发送消息。
- 连接断开:
- Protocol.connectionLost()
- ClientFactory.clientConnectionLost()
即 建立连接时先执行 ClientFactory 中的回调,然后执行 Protocol 中的回调,而连接断开时则正好相反。
Twisted 综述的更多相关文章
- Mina、Netty、Twisted一起学(八):HTTP服务器
HTTP协议应该是目前使用最多的应用层协议了,用浏览器打开一个网站就是使用HTTP协议进行数据传输. HTTP协议也是基于TCP协议,所以也有服务器和客户端.HTTP客户端一般是浏览器,当然还有可能是 ...
- Twisted随笔
学习了socket后决定尝试使用框架,目标锁定了Twisted. 什么是Twisted? twisted是一个用python语言写的事件驱动的网络框架,他支持很多种协议,包括UDP,TCP,TLS和其 ...
- Python爬虫入门一之综述
大家好哈,最近博主在学习Python,学习期间也遇到一些问题,获得了一些经验,在此将自己的学习系统地整理下来,如果大家有兴趣学习爬虫的话,可以将这些文章作为参考,也欢迎大家一共分享学习经验. Pyth ...
- Python 安装Twisted 提示python version 2.7 required,which was not found in the registry
由于我安装Python64位的,下载后没注册,安装Twisted时老提示“python version 2.7 required,which was not found in the registry ...
- Python - twisted web 入门学习之一
原文地址:http://zhouzhk.iteye.com/blog/765884 python的twisted框架中带了一个web server: twisted web.现在看看怎么用. 一)准备 ...
- Twisted
Twisted是一个事件驱动的网络框架,其中包含了诸多功能,例如网络协议,线程,数据库管理,网络操作,电子邮件等 事件驱动 一,注册事件 二,触发事件 自定义事件框架 event_fram.py # ...
- Mina、Netty、Twisted一起学(十):线程模型
要想开发一个高性能的TCP服务器,熟悉所使用框架的线程模型非常重要.MINA.Netty.Twisted本身都是高性能的网络框架,如果再搭配上高效率的代码,才能实现一个高大上的服务器.但是如果不了解它 ...
- Mina、Netty、Twisted一起学(九):异步IO和回调函数
用过JavaScript或者jQuery的同学都知道,JavaScript特别是jQuery中存在大量的回调函数,例如Ajax.jQuery的动画等. $.get(url, function() { ...
- Mina、Netty、Twisted一起学(七):发布/订阅(Publish/Subscribe)
消息传递有很多种方式,请求/响应(Request/Reply)是最常用的.在前面的博文的例子中,很多都是采用请求/响应的方式,当服务器接收到消息后,会立即write回写一条消息到客户端.HTTP协议也 ...
随机推荐
- Unity手游引擎安全解析及实践
近日,由Unity主办的"Unity技术开放日"在广州成功举办,网易移动安全技术专家卓辉作为特邀嘉宾同现场400名游戏开发者分享了网易在手游安全所积累的经验.当下,很多手游背后都存 ...
- 洛谷P2532 [AHOI2012]树屋阶梯(Catalan数)
P2532 [AHOI2012]树屋阶梯 题目描述 输入输出格式 输入格式: 一个正整数N(1<=N<=500),表示阶梯的高度. 输出格式: 一个正整数,表示搭建方法的个数.(注:搭建方 ...
- python中的三元表达式(三目运算符)
python中没有其他语言中的三元表达式,不过有类似的实现方法 其他语言中,例如java的三元表达式是这样 int a = 1; String b = ""; b = a > ...
- react-native-contact 安卓已测试,
1. 下载模块 npm install react-native-contacts --save 2.安卓配置: a.在android/settings.gradle include ':rea ...
- 黑马学习AJAX jQuery发送异步请求 $.ajax() $.post() $.get()是在调用方法而不是定义方法
- mac 安装cocoapods
按主command+空格 输入ter 就能看到终端 左键单机(直接点回车键也可以)打开即可 需要先安装ruby环境 安装rvm curl -sSL https://get.rvm.io | bash ...
- mysql5.7安装部署后初始密码查看以及修改
一.查看初始密码以下两种方法: 1.找到自己的error.log日志文件,执行自己的命令,红色标记的部分为初始化密码. grep 'temporary password' /data/mysql/er ...
- python进阶08 MySQL基础补充
python进阶08 MySQL基础补充 本次课程都是基于三张表格的使用 一.子查询 #如何找到‘张三’的成绩 #思路:先找到张三的学号,在拿这个张三的学号到成绩表里面去匹配,得出成绩 #如何用一条查 ...
- Codeforces 1167F(计算贡献)
要点 容易想到排序,然后对于每个数: 人的惯性思维做法是:\(a[i]*(rank1的+rank2的+-)\).然而解法巧妙之处在于直接把所有的加和当成一个系数,然后先假装所有情况系数都是1,接着往上 ...
- NET Core中使用Redis和Memcached
.NET Core中使用Redis和Memcached的序列化问题 前言 在使用分布式缓存的时候,都不可避免的要做这样一步操作,将数据序列化后再存储到缓存中去. 序列化这一操作,或许是显式的,或许 ...