18.7. asynchat
— 异步 socket 指令/响应 处理器¶
源代码: Lib/asynchat.py
3.6 版后已移除: 请使用 asyncio
替代。
注解
该模块仅为提供向后兼容。我们推荐在新代码中使用 asyncio
。
此模块在 asyncore
框架之上构建,简化了异步客户端和服务器并使得处理元素为以任意字符串结束或者为可变长度的协议更加容易。 asynchat
定义了一个可以由你来子类化的抽象类 async_chat
,提供了 collect_incoming_data()
和 found_terminator()
等方法的实现。 它使用与 asyncore
相同的异步循环,并且可以在通道映射中自由地混合 asyncore.dispatcher
和 asynchat.async_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
对象的方法就会被事件处理框架调用而无须程序员方面做任何操作。两个可被修改的类属性,用以提升性能,甚至也可能会节省内存。
-
ac_in_buffer_size
¶ 异步输入缓冲区大小 (默认为
4096
)。
-
ac_out_buffer_size
¶ 异步输出缓冲区大小 (默认为
4096
)。
与
asyncore.dispatcher
不同,async_chat
允许你定义一个 FIFO 队列 producers。 其中的生产者只需要一个方法more()
,该方法应当返回要在通道上传输的数据。 生产者通过让其more()
方法返回空字节串对象来表明其处于耗尽状态 (意即 它已不再包含数据)。 此时async_chat
对象会将该生产者从队列中移除并开始使用下一个生产者,如果有下一个的话。 当生产者队列为空时handle_write()
方法将不执行任何操作。 你要使用通道对象的set_terminator()
方法来描述如何识别来自远程端点的入站传输的结束或是重要的中断点。要构建一个可用的
async_chat
子类,你的输入方法collect_incoming_data()
和found_terminator()
必须要处理通道异步接收的数据。 这些参数的描述见下文。-
-
async_chat.
close_when_done
()¶ 将
None
推入生产者队列。 当此生产者被弹出队列时它将导致通道被关闭。
-
async_chat.
collect_incoming_data
(data)¶ 调用时附带 data,其中包含任意数量的已接收数据。 必须被重载的默认方法将引发一个
NotImplementedError
异常。
-
async_chat.
discard_buffers
()¶ 在紧急情况下此方法将丢弃输入和/或输出缓冲区以及生产者队列中的任何数据。
-
async_chat.
found_terminator
()¶ 当输入数据流能匹配
set_terminator()
所设定的终结条件时会被调用。 必须被重载的默认方法将引发一个NotImplementedError
异常。 被缓冲的输入数据应当可以通过实例属性来获取。
-
async_chat.
get_terminator
()¶ 返回通道的当前终结器。
-
async_chat.
push
(data)¶ 将数据推入通道的队列以确保其被传输。 要让通道将数据写到网络中你只需要这样做就足够了,虽然以更复杂的方式使用你自己的生产者也是有可能的,例如为了实现加密和分块。
-
async_chat.
push_with_producer
(producer)¶ 获取一个生产者对象并将其加入到与通道相关联的生产者队列中。 当所有当前已推入的生产者都已被耗尽时通道将通过调用其
more()
方法来耗用此生产者的数据并将数据发送至远程端点。
-
async_chat.
set_terminator
(term)¶ 设置可在通道上被识别的终结条件。
term
可以是三种类型值中的任意一种 ,对应于处理入站协议数据的三种不同方式。term
描述
string
当在输入流中发现该字符串时将会调用
found_terminator()
integer
当接收到指定数量的字符时将会调用
found_terminator()
None
通道会不断地持续收集数据
请注意终结器之后的任何数据将可在
found_terminator()
被调用后由通道来读取。
18.7.1. asynchat 示例¶
下面的例子片段显示了如何通过 async_chat
来读取 HTTP 请求。 Web 服务器可以为每个入站的客户端连接创建 http_request_handler
对象。 请注意在初始时通道终结器会被设置为匹配 HTTP 请求头末尾的空行,并且会用一个旗标来指明请求头正在被读取。
一旦完成了标头的读取,如果请求类型为 POST (表明输入流中存在更多的数据) 则会使用 Content-Length:
标头来设置一个数值终结器以从通道读取适当数量的数据。
一旦完成了对所有相关输入的处理,将会在设置通道终结器为 None
以确保忽略掉 Web 客户端所发送的任何无关数据之后调用 handle_request()
方法。
import asynchat
class http_request_handler(asynchat.async_chat):
def __init__(self, sock, addr, sessions, log):
asynchat.async_chat.__init__(self, sock=sock)
self.addr = addr
self.sessions = sessions
self.ibuffer = []
self.obuffer = b""
self.set_terminator(b"\r\n\r\n")
self.reading_headers = True
self.handling = False
self.cgi_data = None
self.log = log
def collect_incoming_data(self, data):
"""Buffer the data"""
self.ibuffer.append(data)
def found_terminator(self):
if self.reading_headers:
self.reading_headers = False
self.parse_headers(b"".join(self.ibuffer))
self.ibuffer = []
if self.op.upper() == b"POST":
clen = self.headers.getheader("content-length")
self.set_terminator(int(clen))
else:
self.handling = True
self.set_terminator(None)
self.handle_request()
elif not self.handling:
self.set_terminator(None) # browsers sometimes over-send
self.cgi_data = parse(self.headers, b"".join(self.ibuffer))
self.handling = True
self.ibuffer = []
self.handle_request()