介绍
此模块在asyncore架构上建立,简化了异步客户端和服务器,并且使得处理元素为任意字符串结束或者为变长度的协议更加容易。asynchat定义了一个可以由使用者来子类化的抽象类async_chat,提供了collect_incoming_data()和found_terminator()等方法的实现。它使用与asyncore相同的异步循环,并且可以在通道映射中自由地混合asyncore.dispatcher和asynchat.aync_chat这两种类型地通道。一般来说asyncore.dispatcher服务器通道在接收到传入地连接请求时,会生成新地asynchat.async_chat通道对象。
class asynchat.async_chat
这个类是asyncore.dispatcher的抽象子类。对于实际使用的代码必须子类化async_chat,提供有意义的collect_incoming_data()和found_terminator()方法。asyncore.dispatcher的方法也可以被使用,但它们在消息/响应上下文中并不是全都有意义。
与asyncore.dispatcher类似,async_chat也定义了一组通过对select()调用之后的套接字进行分析所生成的事件。一旦启动轮询循环,async_chat对象的方法就会被事件处理框架调用而无需程序员方法做任何操作。
两个可被修改的类属性,用以提升性能,甚至也可能会节省内存。
1)ac_in_buffer_size:异步输入缓冲区大小(默认:4096)
2)ac_out_buffer_size:移除输出缓冲区大小。
与asyncore.dispatcher不同,async_chat允许你定义一个FIFO队列producer。其中的生产者只需要一个方法more(),该方法应当返回要在通道上传递的数据。生成者通过让其more()方法返回空字节串对象来表明其处于耗尽状态(它已不再包含数据)。此时,async_chat对象会将该生产者从队列中移除并开始使用下一个生产者,如果有下一个的话。当生产者队列为空时,handle_write()方法将不执行任何操作。你要使用通道对象的set_terminator()方法来描述如何识别来自远程端点的入站传输的结束或者重要的中断点。
要构建一个可用的async_chat子类,你的输入方法collect_incoming_data()和found_terminator()必须要处理通道异步接收的数据。
1)async_chat.close_when_done():将None推入生产者队列。当此生产者被弹出队列时,它将导致通道被关闭。
2)async_chat.collect_incoming_data(data):调用时附带data,其中包含任意数量的已接收数据。必须被重载的默认方法将引发一个NotImplementedError异常。
3)asyn_chat.discard_buffer():在紧急情况下,此方法将丢弃输入和/或输出缓冲区以及生产者队列中的任何数据。
4) asyn_chat.found_terminator():当输入数据流能匹配set_terminator()所设定的终结条件时被调用。必须被重载的默认方法将引发一个NotImplementedError异常。被缓存的输入数据应当可以通过实例属性来获取。
5)async_chat.get_terminator():返回通道的当前终止符。
6)async_chat.push(data):将数据推入通道的队列以确保其被传输。要让通道将数据写到网络中,只需要这样做就足够。
7)async_chat.push_with_producer(producer)
8)async_chat.set_terminator(term):设置可在通道上被识别的终结条件。term可以是三种类型值中的任何一种,对应于处理入站协议数据的三种不同方式。
术语 | 描述 |
string | 当在输入流中发现这个字符串,将调用found_terminator() |
integer | 在接收到指定数目字符时,将调用found_terminator() |
None | 通道一直持续收集数据。 |
注意:在found_terminator()后,在终止符之后的任何数据将可用于由这个通道读取。
以下用一个示例展示使用 asynchat模块实现一个tcp服务:
服务器具有以下特性:
1) 识别一个以\r为终止符的字符串
2)它接收到字符串1返回One, 接收2返回Two,接收到3返回Three,接收到其它字符串时,返回Hello: + 接收到的字符串
3) 返回字符串以\r\n为终止符
源代码如下:
#!/usr/bin/env python3
import os
import sys
import asynchat
import asyncore
import socket
DEFAULT_PORT = 31337
class Echo:
def parseCommand(self, num):
if num == "1":
return "One"
elif num == "2":
return "Two"
elif num == "3":
return "Three"
else:
return "Hello: " + num
class ConnectionDispatcher(asyncore.dispatcher):
def __init__(self, port, echo):
# 创建套接字,绑定IP地址和监听端口,开始监听
asyncore.dispatcher.__init__(self)
self.port = port
self.device = echo
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.set_reuse_addr()
self.bind(("", port))
self.listen(5)
# 接受连接,调用服务处理方法
def handle_accept(self):
client_info = self.accept()
print("client connected: ", client_info )
ConnectionHandler(client_info[0], self.device)
class ConnectionHandler(asynchat.async_chat):
# 设置输入终止符
def __init__(self, sock, device):
asynchat.async_chat.__init__(self, sock)
self.set_terminator(b"\r")
# 输出终止符格式
self.outputTerminator = "\r\n"
self.device = device
self.buffer = ""
# 收集数据
def collect_incoming_data(self, data):
self.buffer = self.buffer + data.decode()
# 查找终止符,并把终止符之前的数据交给处理方法
def found_terminator(self):
data = self.buffer
self.buffer = ""
self.handleClientRequest(data)
def handleClientRequest(self, request):
request = request.strip()
# 显示接收到的数据
print(request)
response = self.device.parseCommand(request)
# 回应客户端
if response != None:
self.sendClientResponse("{}".format(response))
return
def sendClientResponse(self, response=""):
data = response + self.outputTerminator
self.push(data.encode())
if __name__ == "__main__":
if sys.version_info < (3,0,0) and sys.version_info < (3,12,0):
sys.stderr.write("You need Python 3.0 or later (but less than 3.12) to run this script\n")
input("Press enter to quit... ")
sys.exit(1)
# 尝试运行服务器
try:
echo = Echo()
server = ConnectionDispatcher(DEFAULT_PORT, echo)
asyncore.loop()
except Exception as e:
if isinstance(e, SystemExit):
raise e
else:
print("Error: {}".format(e))
sys.exit(1)
运行以上python代码。
以下是测试结果: