Twisted
- 什么是Twisted?
- 为什么使用twisted?
- Twisted 写TCP通信基本实例
- - TCP服务端
- - TCP客户端
- Twisted的Deferred机制
- Why Deferred?
- Deferred TCP-ECHO客户端实现
- - TCP client为例,
什么是Twisted?
Twisted是一种非阻塞的网络服务器技术,通过事件循环处理、回调机制来触发相应操作。
【相比socket通信,当有多个事件时,如果有一个事件阻塞,就都阻塞的情况。】
- 异步处理:
类似小学数学题中的给定时间任务的合理安排,比如烧水和做饭两个任务,不用等烧完水再做饭,而是烧水中去做饭,一开始烧水,中间去做饭烧开时再管热水。
- 📕twisted原理:
- reactor中心
- 多任务轮询
- 暂停循环回调执行操作
为什么使用twisted?
网络编程中,最基本的对协议的包装为socket库。socket通信方式如下:
Tcp协议中,服务端等待客户端发送数据rece()才能收到,否则就在阻塞等待。客户端也一样要等待服务端发送。这就是阻塞IO,浪费了很多性能。
Twisted 进行改进,提出非阻塞IO,不等待回复,而是不断轮询看是否收到。并根据情况进行状态切换操作,这就是异步处理.
- 因此有多个客户端时,不用多线程实现,twisted内部单线程回调都能实现[^1]
Twisted 写TCP通信基本实例
- 📕Twisted使用方式
- protocol中定义事件回调操作(根据需要找已有的方法即可)
- 在factory中注册
- 开启监听、轮询run
- TCP服务端
# TCP服务端
import twisted
import twisted.internet.protocol
import twisted.internet.reactor
SERVER_PORT = 8080 # 监听端口
class Server(protocol.Protocol): # 定义好事件的回调操作程序,twisted定义的事件,根据需要找到
def connectionMade(self): # 客户端连接的时候触发
print("客户端地址: %s" % self.transport.getPeer().host)
def dataReceived(self. data): # 接收客户端数据
print("[服务端]接收到数据 : %s" % data.decode("UTF-8")) # 输出接收到的数据
self.transport.write(("[ECHO] %s" % data.decode("UTF-8")).encode("UTF-8")) # 回应
class DefaultServerFactory(protocol.Factory): # 定义处理工厂类,注册
protocol = Server
def main():
reactor.listenTCP(SERVER_PORT, DefaultServerFactory()) # 服务监听
print("服务启动完毕,等待客户端连接。。。")
reactor.run() # 事件轮询
if __name__ == "__main__":
main()
- TCP客户端
import twisted
import twisted.internet.protocol
import twisted.internet.reactor
SERVER_HOST = "localhost" # server 主机
SERVER_PORT = 8080 # server端口号
class Client(protocol.Protocol): # 定义客户端回调处理
def connectionMade(self):
print("服务器连接成功,可以进行数据交互,如果要结束通讯,则直接回车")
self.send()
def send(self): # 自定义的数据发送方法
input_data = input("请输入要发送的数据")
if input_data: # 有数据输入
self.transport.write(input_data.encode("UTF-8"))
else:
self.transport.loseConnection() # 关闭连接
class DefaultClientFactory(protocol.ClientFactory):
protocol = Client
clientConnectionLost = clientConnectionFailed = lambda self, connector, reason:reactor.stop() # 只要连接失败 就关闭reactor
def main():
reactor.connectTCP(SERVER_HOST, SERVER_PORT, DefaultClientFactory())
reactor.run()
if __name__ == "__main__":
main()
connectionMade()等有的函数是Twisted定义好的方法,根据需要调用即可。
从代码中可以看出Twisted优势:在运行多个客户端时,相比socket,这里不需要进行并发(多线程)开发,全部执行流程都是单线程的运行模式(Python中的多线程有GIL全局锁问题)。Twisted内部会进行单线程回调实现,只需要找到对应的事件(方法)即可
Twisted的Deferred机制
Why Deferred?
线程不被长时间占用,而是另外开启线程执行这个长时任务,执行完告诉。
Deferred TCP-ECHO客户端实现
- TCP client为例,
import twisted
import twisted.internet.protocol
import twisted.internet.defer
import twisted.internet.reactor
import twisted.internet.threads
import time
SERVER_HOST = "localhost" # server 主机
SERVER_PORT = 8080 # server端口号
class DeferClient(protocol.Protocol): # 设置一个回调处理类
def connectionMade(self):
print("服务器连接成功,可以进行数据交互,如果要结束通讯,则直接回车")
self.send()
def send(self): # 自定义的数据发送方法
input_data = input("请输入要发送的数据")
if input_data: # 有数据输入
self.transport.write(input_data.encode("UTF-8"))
else:
self.transport.loseConnection() # 关闭连接
def dataReceived(self, data): # 接收服务端发送的数据
content = data.decode("utf-8")
threads.deferToThread(self.handle_request, content).addCallback(self.handle_success) # 开启另一线程并用回调提示完成
def handle_request(self, content): # 数据处理过程
print("客户端对服务端的数据 %s 进行处理,此处会产生1s延迟..." % content) # 处理完毕后的信息输出
time.sleep(1) # 模拟延迟
return content # 返回处理结果
def handle_success(self, result):
print(处理完成,进行参数接收 %s" % result) # 处理完毕后的信息输出
def handle_error(self, exp):
print("程序出错,%s" % exp)
class DefaultClientFactory(protocol.ClientFactory):
protocol = DeferClient
clientConnectionLost = clientConnectionFailed = lambda self, connector, reason:reactor.stop() # 只要连接失败 就关闭reactor
def main():
reactor.connectTCP(SERVER_HOST, SERVER_PORT, DefaultClientFactory())
reactor.run()
if __name__ == "__main__":
main()
解释:
对数据的处理需要时间,想不只等待而是先去做别的:
def handle_request(self, content): # 数据处理过程
print("客户端对服务端的数据 %s 进行处理,此处会产生1s延迟..." % content) # 处理完毕后的信息输出
time.sleep(1) # 模拟延迟
return content # 返回处理结果
因此把数据放到另一线程中进行处理,并用defer回调提示完成
import twisted.internet.threads
threads.deferToThread(self.handle_request, content).addCallback(self.handle_success) # 开启另一线程并用回调提示完成
注:以上总结自b站这个视频