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协议也 ...
随机推荐
- python split 与join
1.string.join (saq):以string 为分隔符,将seq中所有的元素(字符串表示"")合并成一个新的字符串 2.string.split(str="&q ...
- 51nod1105(二分)
题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1105 题意:中文题诶- 思路:直接二分答案,再通过二分找有多少 ...
- 为产品接入微信支付解决方案(公司&个人)
微信支付个人及公司的区别 H5支付:主要实现的效果是在非微信内网页中点击支付,拉起微信APP内的微信支付进行付款. APP支付:主要实现的效果是在APP内部点击支付,拉起微信APP内的微信支付进行付款 ...
- servlet连接mysql数据库和oracle数据库
连接mysql数据库 package dao; import java.sql.Connection; import java.sql.DriverManager; import java.sql.P ...
- Docker学习:virtualbox安装和配置
下载.安装 从官网:https://www.virtualbox.org/下载,根据说明直接一步步安装即可 安装ubuntu 说明:这里本机内存是16G,若内存<4G安装完成虚拟机, 安装完成之 ...
- centOS-7.5上安装redis-5.0.0
- HDU-2586-How far away(LCA Tarjan离线算法)
链接:https://vjudge.net/problem/HDU-2586 题意: 勇气小镇是一个有着n个房屋的小镇,为什么把它叫做勇气小镇呢,这个故事就要从勇气小镇成立的那天说起了,修建小镇的时候 ...
- A.dreamstart的催促
题目描述 有一天集训队的学弟们正在计算一堆数,但是dreamstart感觉他们算的太慢了,就让他们坐在一起想出一个快速计算的方法,但是由于他们一时想不出来,想让你帮助他们.他们说现在有一个数列,要算出 ...
- 2017浙江工业大学-校赛决赛 BugZhu抽抽抽!!
Description 当前正火的一款手游阴阳师又出新式神了,BugZhu十分想要获得新出的式神,所以他决定花光所有的积蓄来抽抽抽!BugZhu经过长时间的研究后发现通过画三角外接圆能够提高获得该式神 ...
- eclipse plugin
快速查看目录 org.sf.easyexplore_1.0.4.jar mongo DB net.jumperz.app.MMonjaDB_1.0.16.jar jasper report jaspe ...