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协议也 ...
随机推荐
- JSON parse error: Cannot deserialize value of type `java.time.LocalDateTime` from String
在使用Postman测试Spring Boot项目接口时,接口返回JSON parse error: Cannot deserialize value of type `java.time.Local ...
- Hyperledger Fabric 第一次安装
第一次安装fabric有很多坑.记录一下,主要跟版本问题. 参考的是http://www.cnblogs.com/aberic/p/7532114.html 这篇博客. 我用的阿里云centOs 7. ...
- 洛谷P1132 数字生成游戏
P1132 数字生成游戏 题目描述 小明完成了这样一个数字生成游戏,对于一个不包含0的数字s来说,有以下3种生成新的数的规则: 将s的任意两位对换生成新的数字,例如143可以生成314,413,134 ...
- sublime text 3 的emmet 添加自定义 html 片段
比如想在html写 jquery 直接添加jquery地址,打开 ‘首选项->Package Setting->Emmet->Settings - User’, css也可以如法炮制 ...
- Angular.js思维导图
AngularJS的四大特性的思维导图如下: 将AngularJS应用于工作:其思维导图如下: AngularJS服务思维导图:
- JavaWeb之用户数据回显
- 微信小程序 笔记
1.Input 输入控件 <input type='digit' placeholder='0.00'></input> 如果要使用单纯的数字控件,使那么可以将type设置为d ...
- 【填坑】loj6159. 「美团 CodeM 初赛 Round A」最长树链
水一水 枚举各个质数,把是这个数倍数的点留下,跑直径,没了 #include <bits/stdc++.h> using namespace std; int h,t,n,p,q,M,N; ...
- NET Core写了一个轻量级的Interception框架[开源]
NET Core写了一个轻量级的Interception框架[开源] ASP.NET Core具有一个以ServiceCollection和ServiceProvider为核心的依赖注入框架,虽然这只 ...
- log4j.properties错误及配置详解
当在Eclipse上运行MapReduce程序遇到以上问题时,请检查项目中是否有log4j.properties配置文件,或者配置文件是否正确. 刚接触Hadoop的时候不太了解log4j.prope ...