asynchat — 异步套接字命令/响应处理程序 — Python 文档

来自菜鸟教程
Python/docs/3.9/library/asynchat
跳转至:导航、​搜索

asynchat — 异步套接字命令/响应处理程序

源代码: :source:`Lib/asynchat.py`

自 3.6 版起已弃用:请改用 asyncio



笔记

该模块的存在只是为了向后兼容。 对于新代码,我们建议使用 asyncio


该模块建立在 asyncore 基础架构上,简化了异步客户端和服务器,并使其更容易处理元素由任意字符串终止或长度可变的协议。 asynchat 定义了您子类化的抽象类 async_chat,提供了 collect_incoming_data()found_terminator() 方法的实现。 它使用与asyncore相同的异步循环,两种类型的通道asyncore.dispatcherasynchat.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 允许您定义 producers 的 FIFO 队列。 生产者只需要一种方法,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 可以是三种类型的值中的任何一种,对应三种不同的方式来处理传入的协议数据。

学期

描述

细绳

当在输入流中找到字符串时将调用 found_terminator()

整数

当接收到指定数量的字符时,将调用 found_terminator()

None

频道永远持续收集数据

请注意,在调用 found_terminator() 后,终止符后面的任何数据都可供通道读取。

异步示例

以下部分示例显示了如何使用 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()