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

源代码: Lib/asynchat.py


此模块构建在asyncore基础结构的基础上,简化了异步 Client 端和服务器,并使处理其元素以任意字符串终止或长度可变的协议变得更加容易。 asynchat定义了子类的抽象类async_chat,提供了collect_incoming_data()found_terminator()方法的实现。它使用与asyncore相同的异步循环,并且两种类型的通道asyncore.dispatcherasynchat.async_chat可以在通道图中自由混合。通常,asyncore.dispatcher服务器通道在接收到传入的连接请求时会生成新的asynchat.async_chat通道对象。

asyncore.dispatcher一样,async_chat定义了一组事件,这些事件是pass对select()调用后的套接字条件的分析生成的。一旦轮询循环开始,事件处理框架将调用async_chat对象的方法,而程序员无需采取任何措施。

可以修改两个类的属性,以提高性能,甚至可能节省内存。

  • ac_in_buffer_size

    • 异步 Importing 缓冲区的大小(默认为4096)。
  • ac_out_buffer_size

    • 异步输出缓冲区大小(默认为4096)。

asyncore.dispatcher不同,async_chat允许您定义* producer 的先进先出队列(fifo)。生产者仅需要一种方法more(),该方法应返回要在通道上传输的数据。生产者pass使其more()方法返回空字符串来指示耗尽(,即*它不包含更多数据)。此时,async_chat对象将从 fifo 中删除生产者,并开始使用下一个生产者(如果有)。当生产者 fifo 为空时,handle_write()方法不执行任何操作。您使用通道对象的set_terminator()方法来描述如何识别来自远程端点的传入传输的结尾或其中的重要断点。

要构建Function正常的async_chat子类,您的 Importing 法collect_incoming_data()found_terminator()必须处理通道异步接收的数据。方法如下所述。

  • async_chat. close_when_done ( )

    • None推送到制作人 fifo。当此生产者从 fifo 弹出时,它将导致通道被关闭。
  • async_chat. collect_incoming_data(* data *)

    • pass* data *进行调用,该数据保存任意数量的接收数据。必须重写的默认方法会引发NotImplementedError异常。
  • async_chat. discard_buffers ( )

    • 在紧急情况下,此方法将丢弃保存在 Importing 和/或输出缓冲区以及生产者 fifo 中的所有数据。
  • async_chat. found_terminator ( )

    • 当 Importing 数据流符合set_terminator()设置的终止条件时调用。必须重写的默认方法会引发NotImplementedError异常。缓冲的 Importing 数据应pass instance 属性可用。
  • async_chat. get_terminator ( )

    • 返回通道的当前终止符。
  • async_chat. push(* data *)

    • 将数据推送到通道的 fifo 以确保其传输。这是使通道将数据写到网络所需要做的全部工作,尽管例如,可以在更复杂的方案中使用自己的生产方来实现加密和分块。
  • async_chat. push_with_producer(Producer)

    • 获取生产者对象,并将其添加到与通道关联的生产者 fifo。当所有当前推送的生产者都用尽后,通道将pass调用其more()方法消耗该生产者的数据,并将数据发送到远程端点。
  • async_chat. set_terminator(* term *)

    • 设置要在通道上识别的终止条件。 term可以是三种类型的值中的任何一种,它们对应于处理传入协议数据的三种不同方式。
termDescription
string在 Importing 流中找到字符串时将调用found_terminator()
integer收到指定数量的字符后将呼叫found_terminator()
NoneChannel 将永远永远收集数据

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

17.7.1. asynchat-辅助类

    • class * asynchat. fifo([* list = None *])
    • fifo保留已由应用程序推送但尚未弹出以写入通道的数据。 fifo是用于保存数据和/或生产者直到需要它们之前的列表。如果提供了* list *参数,则它应包含要写入通道的生产者或数据项。
  • is_empty ( )

    • 当且仅当 fifo 为空时,返回True
  • first ( )

    • 返回来自 fifo 的最近的push()版本。
  • push(* data *)

    • 将给定的数据(可以是字符串或生产者对象)添加到生产者 fifo。
  • pop ( )

    • 如果 fifo 不为空,则返回True, first(),删除弹出的项目。返回False, None表示一个空的 fifo。

17.7.2. asynchat 示例

以下部分示例显示了如何使用async_chat读取 HTTP 请求。 Web 服务器可能会为每个传入的 Client 端连接创建http_request_handler对象。请注意,最初将通道终止符设置为与 HTTPHeaders 末尾的空白行匹配,并且有一个标志指示正在读取 Headers。

读取 Headers 后,如果请求的类型为 POST(指示 Importing 流中存在其他数据),则Content-Length:Headers 用于设置数字终止符,以从通道中读取正确数量的数据。

将通道终止符设置为None之后,一旦将所有相关 Importing 整理完毕,就会调用handle_request()方法,以确保忽略 WebClient 端发送的任何无关数据。

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