Source code: Lib/asynchat.py
这个模块建立在 asyncore 基础结构上,简化了异步客户端和服务器,使得处理带有以任意字符串终止或者可变长度的元素的协议更加容易。asynchat 定义了你子类化的抽象类 async_chat ,提供了 collect_incoming_data() 和 found_terminator() 方法的实现。它使用了和 asyncore一样的异步循环,和两种类型的通道, asyncore.dispatcher 和 asynchat.async_chat,它们可以被自由地混合在通道映射中。当接收传入的连接请求时,一个 asyncore.dispatcher 服务器通道往往会产生新的 asynchat.async_chat 通道对象。
这个类是一个 asyncore.dispatcher 的抽象类。为了实际使用代码,你必须要子类化 async_chat,提供有意义的 collect_incoming_data() 和 found_terminator() 方法。asyncore.dispatcher 方法可以被使用,尽管不是所有的在消息/响应上下文都有意义。
像 asyncore.dispatcher一样, async_chat 定义了一套在调用 select() 后套接字状态的分析产生的事件。一旦轮询循环开始,async_chat 对象的方法被事件处理框架调用,with no action on the part of the programmer.
两个类属性可以被修改以提高性能,或者可能甚至节约内存。
异步输入缓存大小 (默认为 4096)。
异步输出缓存大小 (默认为 4096).
不像 asyncore.dispatcher,async_chat 允许你定义一个生产者(producers)的先进先出(fifo)队列。一个生产者需要有一个方法, more(), 这个方法返回将被传送给通道的数据。生产者指出竭尽 (也就是 不包含数据) 会使它的 more() 方法返回空字符串。这时, async_chat 对象会从先进先出队列中移除生产者,并且如果有的话,它会开始使用下一个生产者。当生产者先进先出队列为空时,handle_write() 方法什么也不做。你使用通道对象的 set_terminator() 方法来描述怎样识别来自远程端点的输入传输的末尾,或者重要节点。
为了创建有用的 async_chat 子类,你输入的方法 collect_incoming_data() 和 found_terminator() 必须要异步地处理通道接受的数据。方法描述如下。
压入 None 到生产者先进先出队列中。当这个生产者被弹出先进先出队列时会使得通达被关闭。
当任意数量的data到达后被调用。该方法为默认方法,必须被重写。会引发NotImplementedError 异常。
在紧急情况时这个方法会丢弃任何保留在生产者先进先出队列的输入以及/或者输出缓存。
当输入数据流符合由 set_terminator() 设置的终止条件时被调用。默认方法必须被覆写,会抛出 NotImplementedError 异常。通过实例属性应该使用缓存的输入数据。
返回当前通道的终止符。
向通道的先进先出队列压入数据以确保其传输。这是你要通道向网络写数据所做的全部,不过你可以在更复杂的系统中使用自己的生产者来实现诸如加密和分块。
接受生产者对象做参数,并把它加到与通道相关的生产者先进先出队列中。当所有当前压入的生产者竭尽了通道时会调用more() 来消耗生产者数据并把数据发送到远程端点。
设置在通道上被识别的终止条件。相应与三种不同的处理传入协议数据的方式,term 会使三种不同类型的值。
term | Description |
---|---|
string | Will call found_terminator() when the string is found in the input stream |
integer | Will call found_terminator() when the indicated number of characters have been received |
None | The channel continues to collect data forever |
注意,任何在found_terminator() 被调用之后,跟随在终止符后的数据可以被通道读取。 Note that any data following the terminator will be available for reading by the channel after is called.
下面这个不完整的例子展示了 async_chat如何读取HTTP请求。Web服务器可能会为每一个连入的客户端连线创建一个 http_request_handler 对象。注意,初始时通道终止符被设成与HTTP头部最后的空行相匹配,还有一个用来标识头部是否被读取的标记。
一旦头部被读取,如果请求的类型是POST (指明将来的数据在输入流中呈现) ,然后会使用 Content-Length: 头部来设置一个数字终止符以从通道中读取争取数量的数据。
一旦所有的相关的输入被整理后, handle_request() 方法被调用,设置通道终止符为 None 可以确保额外的由web客户端发送的数据被忽略。
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 = ""
self.set_terminator("\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("".join(self.ibuffer))
self.ibuffer = []
if self.op.upper() == "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, "".join(self.ibuffer))
self.handling = True
self.ibuffer = []
self.handle_request()