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

源代码: Lib / asynchat.py

注意

此模块仅用于向后兼容。对于新代码,我们建议使用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.dispatcherasync_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可以是三种类型的值中的任何一种,对应于处理传入协议数据的三种不同方式。

术语描述
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之后,在所有相关输入已编组之后调用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()