Python的Asyncore非同步Socket模組及實現埠轉發的例子

NO IMAGE

Asyncore模組提供了以非同步的方式寫入套接字服務客戶端和伺服器的基礎結構。

只有兩種方式使一個程式在單處理器上實現“同時做不止一件事”。多執行緒程式設計是最簡單和最流行的方式,但是有另一種很不一樣的技術,可以使得我們保持多執行緒的幾乎所有優勢,卻不用真正使用多執行緒。 如果你的程式主要是受I/O限制的,這是唯一可行的方式。如果你的程式是受處理器限制的,則先發制人的排程執行緒可能是你真正需要的。但是,很少網路伺服器是受處理器限制的。

如果您的作業系統支援在其I / O庫的 select() 系統呼叫(幾乎所有系統都支援),那麼你可以用它一次處理多個通訊通道;當你的I/O在後臺忙碌時處理其他工作。雖然這一策略似乎很奇怪很複雜,尤其是最開始的時候,這在很多方面比多執行緒程式設計更容易理解和控制。asyncore 模組為你解決了很多困難,使你能快速構建複雜的高效能網路伺服器和客戶端。對於會話應用程式和協議, asynchat 模組是非常有用的。

兩個模組背後的想法就是建立一個或者多個網路 通道, 及 asyncore.dispatcher 和 asynchat.async_chat 類的例項. 如果你沒有提供自己的對映的話,建立通道會把這兩個例項加到由 loop() 函式使用的全域性對映中。

一旦初始化通道被建立,呼叫 loop() 函式會啟用通道服務,這會持續到最後一個通道(包括所有在非同步服務中被加到對映中的通道)被關閉。
該模組檔案包含一個loop()函式和一個dispatcher基類,其中loop()函式是全域性函式,負責檢查一個儲存著dispatcher例項的dict,也被稱為channel。
每一個繼承dispatcher類的物件,都可以看作需要處理的socket,因此使用時我們只需定義一個繼承dispatcher的類,然後重寫一些方法就行,一般都是以handle_開頭的方法。

埠轉發的示例
如果你的程式想在同一時間做一件一上的事情,多執行緒是最快也最普遍的方式,但還有一個方式,在I/O流量很大的時候特別實用。如果你的作業系統支援select函式,你就可以讓I/O在後臺讀寫。這個模組聽起來很複雜,但實際上有很多方式可以理解它,這個文件幫你解決了這些問題。
我感覺這個模組應該是一個以事件驅動的非同步I/O,跟C 的事件選擇模型類似。每當發生了讀、寫事件後,會交由我們重寫的事件函式進行處理。
我這裡有一個使用asyncore模組編寫埠轉發指令碼,從這個指令碼可以大概瞭解asyncore的基本使用。
在文章中,所說的客戶端就是我們的電腦,服務端是轉發到的地址。也就是客戶端傳送到這個指令碼的資訊,這個指令碼轉發到服務端上。
首先,定義一個forwarder類:


class forwarder(asyncore.dispatcher):
def __init__(self, ip, port, remoteip,remoteport,backlog=5):
asyncore.dispatcher.__init__(self)
self.remoteip=remoteip
self.remoteport=remoteport
self.create_socket(socket.AF_INET,socket.SOCK_STREAM)
self.set_reuse_addr()
self.bind((ip,port))
self.listen(backlog)
def handle_accept(self):
conn, addr = self.accept()
# print '--- Connect --- '
sender(receiver(conn),self.remoteip,self.remoteport)

這個類繼承自asyncore模組的dispatcher類(它就是我們的主要的類,其中包括了一些之後要過載的函式),建構函式獲得5個引數,第1、2個引數是指令碼監聽的本地IP和埠,第3、4個引數是服務端的IP和埠。第5個引數是listen函式的引數,等待佇列最大長度。
如何使用這個類,只需要如下新建一個物件,把相應IP和埠傳入,再進入loop即可:


forwarder(options.local_ip,options.local_port,options.remote_ip,options.remote_port)
asyncore.loop()

進入loop後相當於開啟了一個守護執行緒,在後臺一直執行著,等待socket事件的發生。
因為我們這個指令碼是埠轉發工具,所以實際上執行的過程是:客戶端連線這個指令碼的埠,讓後傳送給這個埠的資料指令碼自動轉發到服務端地址和埠。所以,首先接收到的應該是連線訊息(accept事件)。
那麼,當accept事件發生後,就進入了handle_accept函式中。所以我們看到,handle_accept函式實際上就是呼叫了accept函式接收了客戶端連線物件和地址。獲得了之後又新建了一個sender類物件,這個物件定義如下:


class sender(asyncore.dispatcher):
def __init__(self, receiver, remoteaddr,remoteport):
asyncore.dispatcher.__init__(self)
self.receiver=receiver
receiver.sender=self
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect((remoteaddr, remoteport))
def handle_connect(self):
pass
def handle_read(self):
read = self.recv(4096)
# print '<-- %04i'%len(read)
self.receiver.to_remote_buffer  = read
def writable(self):
return (len(self.receiver.from_remote_buffer) > 0)
def handle_write(self):
sent = self.send(self.receiver.from_remote_buffer)
# print '--> %04i'%sent
self.receiver.from_remote_buffer = self.receiver.from_remote_buffer[sent:]
def handle_close(self):
self.close()
self.receiver.close()

這個類也是繼承自asyncore.dispatcher,它的建構函式接收3個引數,分別是recv物件(這個之後說到),遠端地址,對應埠。
函式中又新建了一個socket,這個socket就是和服務端埠通訊的socket,然後呼叫connect連線這個埠。
之後其實也是進入了一個等待訊息的過程,因為我們傳送了一個connect,所以下一次接收到的訊息應該是connect,而handle_connect是一個pass掉的函式。沒有執行任何內容。
在連線完成後,我們就相當於建立好了一個埠轉發的通道。當客戶端向這個指令碼監聽的埠傳送資料包時,它就會自動轉發到服務端埠上。服務端埠返回的資料包,會自動轉發到客戶端上。
回到建構函式的第1個引數,我們在forwarder類函式中可以看到,傳入的是一個receiver(conn)物件,receiver也是一個類,我們來看看這個類的定義:


class receiver(asyncore.dispatcher):
def __init__(self,conn):
asyncore.dispatcher.__init__(self,conn)
self.from_remote_buffer=''
self.to_remote_buffer=''
self.sender=None
def handle_connect(self):
pass
def handle_read(self):
read = self.recv(4096)
# print '%04i -->'%len(read)
self.from_remote_buffer  = read
def writable(self):
return (len(self.to_remote_buffer) > 0)
def handle_write(self):
sent = self.send(self.to_remote_buffer)
# print '%04i <--'%sent
self.to_remote_buffer = self.to_remote_buffer[sent:]
def handle_close(self):
self.close()
if self.sender:
self.sender.close()

它也是繼承了asyncore.dispatcher,建構函式只接收一個引數,就是connect的返回值,一個連線物件。
實際上這個物件它就是監聽、處理與客戶端的通訊,而之前說的sender物件是監聽、處理與服務端的通訊。                   

您可能感興趣的文章:

Python中使用select模組實現非阻塞的IO深入理解python中的select模組Python通過select實現非同步IO的方法Python基於select實現的socket伺服器基於python select.select模組通訊的例項講解python select.select模組通訊全過程解析在Python中使用非同步Socket程式設計效能測試Python 網路程式設計起步(Socket傳送訊息)python socket網路程式設計步驟詳解(socket套接字使用)Python網路程式設計之TCP套接字簡單用法示例Python網路程式設計使用select實現socket全雙工非同步通訊功能示例