asyncore --- 異步套接字處理器?

源碼: Lib/asyncore.py

Deprecated since version 3.6, removed in version 3.12: The asyncore module is deprecated (see PEP 594 for details). Please use asyncio instead.


備注

該模塊僅為提供向后兼容。我們推薦在新代碼中使用 asyncio 。

該模塊提供用于編寫(xiě)異步套接字服務(wù)客戶(hù)端與服務(wù)端的基礎構件。

只有兩種方法讓單個(gè)處理器上的程序“同一時(shí)間完成不止一件事”。 多線(xiàn)程編程是最簡(jiǎn)單和最流行的方法,但是還有另一種非常不同的技術(shù),它可以讓你擁有多線(xiàn)程的幾乎所有優(yōu)點(diǎn),而無(wú)需實(shí)際使用多線(xiàn)程。 它僅僅在你的程序主要受 I/O 限制時(shí)有用,那么。 如果你的程序受處理器限制,那么先發(fā)制人的預定線(xiàn)程可能就是你真正需要的。 但是,網(wǎng)絡(luò )服務(wù)器很少受處理器限制。

如果你的操作系統在其 I/O 庫中支持 select() 系統調用(幾乎所有操作系統),那么你可以使用它來(lái)同時(shí)處理多個(gè)通信通道;在 I/O 正在“后臺”時(shí)進(jìn)行其他工作。 雖然這種策略看起來(lái)很奇怪和復雜,特別是起初,它在很多方面比多線(xiàn)程編程更容易理解和控制。 asyncore 模塊為您解決了許多難題,使得構建復雜的高性能網(wǎng)絡(luò )服務(wù)器和客戶(hù)端的任務(wù)變得輕而易舉。 對于“會(huì )話(huà)”應用程序和協(xié)議,伴侶 asynchat 模塊是非常寶貴的。

這兩個(gè)模塊背后的基本思想是創(chuàng )建一個(gè)或多個(gè)網(wǎng)絡(luò ) 通道 ,類(lèi)的實(shí)例 asyncore.dispatcherasynchat.async_chat 。 創(chuàng )建通道會(huì )將它們添加到全局映射中,如果你不為它提供自己的 映射 ,則由 loop() 函數使用。

一旦創(chuàng )建了初始通道,調用 loop() 函數將激活通道服務(wù),該服務(wù)將一直持續到最后一個(gè)通道(包括在異步服務(wù)期間已添加到映射中的任何通道)關(guān)閉。

asyncore.loop([timeout[, use_poll[, map[, count]]]])?

進(jìn)入一個(gè)輪詢(xún)循環(huán),其在循環(huán)計數超出或所有打開(kāi)的通道關(guān)閉后終止。 所有參數都是可選的。 count 形參默認為 None ,導致循環(huán)僅在所有通道關(guān)閉時(shí)終止。 timeout 形參為適當的 select()poll() 調用設置超時(shí)參數,以秒為單位; 默認值為30秒。 use_poll 形參,如果為 True ,則表示 poll() 應優(yōu)先使用 select() (默認為``False``)。

map 形參是一個(gè)條目為所監視通道的字典。 當通道關(guān)閉時(shí)它們會(huì )被從映射中刪除。 如果省略 map,則會(huì )使用一個(gè)全局映射。 通道 (asyncore.dispatcher, asynchat.async_chat 及其子類(lèi)的實(shí)例) 可以在映射中任意混合。

class asyncore.dispatcher?

dispatcher 類(lèi)是對低層級套接字對象的輕量包裝器。 要讓它更有用處,可以從異步循環(huán)調用一些事件處理方法。 在其他方面,它可以被當作是普通的非阻塞型套接字對象。

在特定時(shí)間或特定連接狀態(tài)下觸發(fā)的低層級事件可通知異步循環(huán)發(fā)生了特定的高層級事件。 例如,如果我們請求了一個(gè)套接字以連接到另一臺主機,我們會(huì )在套接字首次變得可寫(xiě)時(shí)得知連接已建立(在此刻你將知道可以向其寫(xiě)入并預期能夠成功)。 包含的高層級事件有:

事件

描述

handle_connect()

由首個(gè)讀取或寫(xiě)入事件引起

handle_close()

由不帶可用數據的讀取事件引起

handle_accepted()

由在監聽(tīng)套接字上的讀取事件引起

在異步處理過(guò)程中,每個(gè)已映射通道的 readable()writable() 方法會(huì )被用來(lái)確定是否要將通道的套接字添加到已執行 select()poll() 用于讀取和寫(xiě)入事件的通道列表中。

因此,通道事件的集合要大于基本套接字事件。 可以在你的子類(lèi)中被重載的全部方法集合如下:

handle_read()?

當異步循環(huán)檢測到通道的套接字上的 read() 調用將要成功時(shí)會(huì )被調用。

handle_write()?

當異步循環(huán)檢測到一個(gè)可寫(xiě)套接字可以被寫(xiě)入時(shí)會(huì )被調用。 通常此方法將實(shí)現必要的緩沖機制以保證運行效率。 例如:

def handle_write(self):
    sent = self.send(self.buffer)
    self.buffer = self.buffer[sent:]
handle_expt()?

當一個(gè)套接字連接存在帶外(OOB)數據時(shí)會(huì )被調用。 這幾乎從來(lái)不會(huì )發(fā)生,因為 OOB 雖然受支持但很少被使用。

handle_connect()?

當活動(dòng)打開(kāi)方的套接字實(shí)際建立連接時(shí)會(huì )被調用。 可能會(huì )發(fā)送一條“歡迎”消息,或者向遠程端點(diǎn)發(fā)起協(xié)議協(xié)商等。

handle_close()?

當套接字關(guān)閉時(shí)會(huì )被調用。

handle_error()?

當一個(gè)異常被引發(fā)并且未獲得其他處理時(shí)會(huì )被調用。 默認版本將打印精簡(jiǎn)的回溯信息。

handle_accept()?

當可以與發(fā)起對本地端點(diǎn)的 connect() 調用的新遠程端點(diǎn)建立連接時(shí)會(huì )在偵聽(tīng)通道(被動(dòng)打開(kāi)方)上被調用。 在 3.2 版中已被棄用;請改用 handle_accepted()。

3.2 版后已移除.

handle_accepted(sock, addr)?

當與發(fā)起對本地端點(diǎn)的 connect() 調用的新遠程端點(diǎn)已建立連接時(shí)會(huì )在偵聽(tīng)通道(被動(dòng)打開(kāi)方)上被調用。 sock 是可被用于在連接上發(fā)送和接收數據的 新建 套接字對象,而 addr 是綁定到連接另一端的套接字的地址。

3.2 新版功能.

readable()?

每次在異步循環(huán)之外被調用以確定是否應當將一個(gè)通道的套接字添加到可能在其上發(fā)生讀取事件的列表中。 默認方法會(huì )簡(jiǎn)單地返回 True,表示在默認情況下,所有通道都希望能讀取事件。

writable()?

每次在異步循環(huán)之外被調用以確定是否應當將一個(gè)通道的套接字添加到可能在其上發(fā)生寫(xiě)入事件的列表中。 默認方法會(huì )簡(jiǎn)單地返回 True,表示在默認情況下,所有通道都希望能寫(xiě)入事件。

此外,每個(gè)通道都委托或擴展了許多套接字方法。 它們大部分都與其套接字的對應方法幾乎一樣。

create_socket(family=socket.AF_INET, type=socket.SOCK_STREAM)?

這與普通套接字的創(chuàng )建相同,并會(huì )使用同樣的創(chuàng )建選項。 請參閱 socket 文檔了解有關(guān)創(chuàng )建套接字的信息。

在 3.3 版更改: familytype 參數可以被省略。

connect(address)?

與普通套接字對象一樣,address 是一個(gè)元組,它的第一個(gè)元素是要連接的主機,第二個(gè)元素是端口號。

send(data)?

data 發(fā)送到套接字的遠程端點(diǎn)。

recv(buffer_size)?

從套接字的遠程端點(diǎn)讀取至多 buffer_size 個(gè)字節。 讀到空字節串表明通道已從另一端被關(guān)閉。

請注意 recv() 可能會(huì )引發(fā) BlockingIOError,即使 select.select()select.poll() 報告套接字已準備好被讀取。

listen(backlog)?

偵聽(tīng)與套接字的連接。 backlog 參數指明排入連接隊列的最大數量且至少應為 1;最大值取決于具體系統(通常為 5)。

bind(address)?

將套接字綁定到 address。 套接字必須尚未被綁定。 (address 的格式取決于具體的地址族 --- 請參閱 socket 文檔了解更多信息。) 要將套接字標記為可重用的 (設置 SO_REUSEADDR 選項),請調用 dispatcher 對象的 set_reuse_addr() 方法。

accept()?

接受一個(gè)連接。 此套接字必須綁定到一個(gè)地址上并且偵聽(tīng)連接。 返回值可以是 None 或一個(gè) (conn, address) 對,其中 conn 是一個(gè)可用來(lái)在此連接上發(fā)送和接收數據的 新的 套接字對象,而 address 是綁定到連接另一端套接字的地址。 當返回 None 時(shí)意味著(zhù)連接沒(méi)有建立,在此情況下服務(wù)器應當忽略此事件并繼續偵聽(tīng)后續的入站連接。

close()?

關(guān)閉套接字。 在此套接字對象上的后續操作都將失敗。 遠程端點(diǎn)將不再接收任何數據(在排入隊列的數據被清空之后)。 當套接字被垃圾回收時(shí)會(huì )自動(dòng)關(guān)閉。

class asyncore.dispatcher_with_send?

dispatcher 的一個(gè)添加了簡(jiǎn)單緩沖輸出功能的子類(lèi),適用于簡(jiǎn)單客戶(hù)端。 對于更復雜的用法請使用 asynchat.async_chat。

class asyncore.file_dispatcher?

file_dispatcher 接受一個(gè)文件描述符或 file object 以及一個(gè)可選的 map 參數,并對其進(jìn)行包裝以配合 poll()loop() 函數使用。 如果提供一個(gè)文件對象或任何具有 fileno() 方法的對象,其方法將被調用并傳遞給 file_wrapper 構造器。

可用性: Unix。

class asyncore.file_wrapper?

file_wrapper 接受一個(gè)整數形式的文件描述符并調用 os.dup() 來(lái)復制其句柄,以便原始句柄可以獨立于 file_wrapper 被關(guān)閉。 這個(gè)類(lèi)實(shí)現了足夠的方法來(lái)模擬套接字以供 file_dispatcher 類(lèi)使用。

可用性: Unix。

asyncore 示例基本 HTTP 客戶(hù)端?

下面是一個(gè)非?;镜?HTTP 客戶(hù)端,它使用了 dispatcher 類(lèi)來(lái)實(shí)現套接字處理:

import asyncore

class HTTPClient(asyncore.dispatcher):

    def __init__(self, host, path):
        asyncore.dispatcher.__init__(self)
        self.create_socket()
        self.connect( (host, 80) )
        self.buffer = bytes('GET %s HTTP/1.0\r\nHost: %s\r\n\r\n' %
                            (path, host), 'ascii')

    def handle_connect(self):
        pass

    def handle_close(self):
        self.close()

    def handle_read(self):
        print(self.recv(8192))

    def writable(self):
        return (len(self.buffer) > 0)

    def handle_write(self):
        sent = self.send(self.buffer)
        self.buffer = self.buffer[sent:]


client = HTTPClient('www.python.org', '/')
asyncore.loop()

asyncore 示例基本回顯服務(wù)器?

下面是一個(gè)基本的回顯服務(wù)器,它使用了 dispatcher 類(lèi)來(lái)接受連接并將入站連接發(fā)送給處理程序:

import asyncore

class EchoHandler(asyncore.dispatcher_with_send):

    def handle_read(self):
        data = self.recv(8192)
        if data:
            self.send(data)

class EchoServer(asyncore.dispatcher):

    def __init__(self, host, port):
        asyncore.dispatcher.__init__(self)
        self.create_socket()
        self.set_reuse_addr()
        self.bind((host, port))
        self.listen(5)

    def handle_accepted(self, sock, addr):
        print('Incoming connection from %s' % repr(addr))
        handler = EchoHandler(sock)

server = EchoServer('localhost', 8080)
asyncore.loop()