asynchat --- 異步 socket 指令/響應 處理器?

源代碼: 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 允許你定義一個 FIFO 隊列 producers。 其中的生產者只需要一個方法 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 可以是三種類型值中的任意一種 ,對應于處理入站協議數據的三種不同方式。

term

描述

string

當在輸入流中發現該字符串時將會調用 found_terminator()

integer

當接收到指定數量的字符時將會調用 found_terminator()

None

通道會不斷地持續收集數據

請注意終結器之后的任何數據將可在 found_terminator() 被調用后由通道來讀取。

asynchat 示例?

下面的例子片段顯示了如何通過 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()