# 18.7。 `asynchat` - 异步套接字命令/响应处理器

源代码： Lib/asynchat.py

注意

此模块仅用于向后兼容。对于新代码，我们建议使用`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）。生产者需要只有一个方法，`more()`，它应该返回要在通道上传输的数据。生产者指示疲劳（，即）通过使其`more()`方法返回空字节对象，它不包含更多数据）。此时，`async_chat`对象从fifo中删除生产者，并开始使用下一个生产者（如果有的话）。当生产者fifo为空时，`handle_write()`方法不执行任何操作。您使用通道对象的`set_terminator()`方法来描述如何识别来自远程端点的传入传输的结束或重要断点。

  要构建一个有效的`async_chat`子类，输入方法`collect_incoming_data()`和`found_terminator()`必须处理通道异步接收的数据。方法如下所述。

`async_chat.``close_when_done`()

  将`None`推送到制片人fifo。当这个制片人从fifo弹出时，它导致通道被关闭。

`async_chat.``collect_incoming_data`(data)

  用数据调用，保存任意数量的接收数据。必须重写的默认方法引发一个`NotImplementedError`异常。

`async_chat.``discard_buffers`()

  在紧急情况下，该方法将丢弃保存在输入和/或输出缓冲器和生产者fifo中的任何数据。

`async_chat.``found_terminator`()

  当传入数据流与`set_terminator()`设置的终止条件匹配时调用。必须重写的默认方法引发一个`NotImplementedError`异常。缓冲的输入数据应该通过实例属性可用。

`async_chat.``get_terminator`()

  返回通道的当前终结符。

`async_chat.``push`(data)

  将数据推送到通道的fifo以确保其传输。这是所有你需要做的是让通道将数据写入网络，虽然可以在更复杂的方案中使用自己的生产者来实现加密和分块。

`async_chat.``push_with_producer`(producer)

  获取生产者对象并将其添加到与通道相关联的生成者fifo。当所有当前推送的生产者已经耗尽时，通道将通过调用其`more()`方法来消耗该生产者的数据，并将数据发送到远程端点。

`async_chat.``set_terminator`(term)

  设置要在通道上识别的终止条件。`term`可以是三种类型的值中的任何一种，对应于处理传入协议数据的三种不同方式。

  术语 | 描述
  --- | ---
  字符串 T0> | 当在输入流中找到字符串时，将调用`found_terminator()`
  整数 T0> | 当接收到指定数量的字符时，将调用`found_terminator()`
  `None` | 该频道继续永远收集数据

  注意，在调用`found_terminator()`之后，通道后面的任何数据都可用于读取。

## 18.7.1. asynchat示例

以下部分示例显示如何使用`async_chat`读取HTTP请求。Web服务器可能会为每个传入的客户端连接创建`http_request_handler`对象。请注意，最初，通道终结符设置为与HTTP头末尾的空行相匹配，并且一个标志表示正在读取这些头。

一旦读取了头，如果请求是POST类型（指示输入流中存在更多数据），那么`Content-Length:`头用于设置数字终止符，以读取来自通道的正确数量的数据。

在将通道终结符设置为`None`之后，在所有相关输入已编组之后调用`handle_request()`方法，以确保忽略Web客户端发送的任何无关数据。

    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()