2008年5月20日 星期二

asyncore : Python裡面的非同步Socket

MFC裡面有個Class叫做CAsyncsocket, 它是用實作非同步的socket.

在實際的應用裡面, 一個應用程式很少會固定等待socket連線, 或是傳送資料, 通常都是在突然的情況下一個socket要進行連線, 或是要傳送資料. 所以非同步的socket的好處就在這裡, 你可以指定在socket發生連線或是傳送資料的時候進行某個動作, 而CAsyncsocket就是把這樣的非同步行為包成一個class讓我們只要繼承它就能達到這樣的效果.

而在Python裡面也有個非同步的module叫做asyncore. 我們實作的socket只要繼承asyncore.dispatcher就能進行非同步的socket運作.

下面就有Client和Server的範例code, 主要用途在於Client能連上Server, 然後Client和Server之間就能進行非同步的溝通, Client可以輸入訊息給Server, Server也能輸入訊息給Client.



Server程式 :


# -*- coding: cp950 -*-

import socket
import asyncore
import threading

MAX_RECV = 4096

#負責接受client socket連線
class AgentServer(asyncore.dispatcher):
def __init__(self, port):
#asyncore.dispatcher的constructor
asyncore.dispatcher.__init__(self)
#client socket
self.clientSocket = None
#server port
self.port = port
#建立等待的socket
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.bind(('', self.port))
self.listen(5)

def handle_accept(self):
#接受client socket的連線
self.clientSocket, address = self.accept()
print 'New client from : ' + address[0]
#將和client連線的socket加上一些功能 (自訂socket)
self.clientSocket = ClientAgent(self.clientSocket)

#自訂的client連線socket
class ClientAgent(asyncore.dispatcher):
def __init__(self, socket):
asyncore.dispatcher.__init__(self, socket)
#要送出的data
self.SendData = ""

#從client收到的data
def handle_read(self):
self.RecvData = self.recv(MAX_RECV)
if len(self.RecvData) > 0:
print "recv : " + self.RecvData
#送出data到client
def handle_write(self):
send_byte = self.send(self.SendData)
#一次可能不會全部送完(一次最多送出512 bytes ?)
if send_byte > 0:
send_out = self.SendData[:send_byte]
self.SendData = self.SendData[send_byte:]
self.handle_write()
else:
print "send all!!"
self.SendData = ""

#不自動執行handle_write
def writable(self):
return False

def handle_close(self):
print "close connection : " + self.getpeername()[0]
self.close()
#產生等待client連線的thread
class listen_client_thread(threading.Thread):
def __init__(self,port):
self.agentServer = AgentServer(port)
threading.Thread.__init__ ( self )

def run(self):
print "Listen Client ..."
asyncore.loop()
#產生處理輸入的thread
class input_thread(threading.Thread):
def __init__(self,listen_thread):
self.listen_thread = listen_thread
threading.Thread.__init__ ( self )
def run(self):
while 1:
send_data = raw_input()
self.listen_thread.agentServer.clientSocket.SendData = send_data
self.listen_thread.agentServer.clientSocket.handle_write()

if __name__ == "__main__":
#產生等待client連線的thread
listen_thread = listen_client_thread(111)
listen_thread.start()
#產生處理輸入的thread
input_thread(listen_thread).start()


Client程式 :

# -*- coding: cp950 -*-

import asyncore, socket
import threading

#收到data最大長度
MAX_RECV = 4096

#連線server的socket
class client(asyncore.dispatcher):

def __init__(self, host, port):
asyncore.dispatcher.__init__(self)
self.SendData = ""
self.RecvData = ""
#和server建立連線
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect( (host, port) )

def handle_connect(self):
print 'connect!!'

def handle_close(self):
print "disconnection : " + self.getpeername()[0]
self.close()

#收到的data
def handle_read(self):
self.RecvData = self.recv(MAX_RECV)
if len(self.RecvData) > 0:
print "recv : " + self.RecvData

#送出data
def handle_write(self):
send_byte = self.send(self.SendData)
if send_byte > 0:
send_out = self.SendData[:send_byte]
self.SendData = self.SendData[send_byte:]
print "send : " + send_out
self.handle_write()
else:
print "send all!!"
self.SendData = ""
#自動偵測送出永遠失敗
def writable(self):
return False

#等待server傳送訊息的thread
class send_server_thread(threading.Thread):
def __init__(self,host,port):
self.client = client(host, port)
threading.Thread.__init__ ( self )
def run(self):
try:
asyncore.loop()
except:
pass
#等待user input的thread
class input_thread(threading.Thread):
def __init__(self,client_thread):
self.client_thread = client_thread
threading.Thread.__init__ ( self )
def run(self):
while 1:
send_data = raw_input()
self.client_thread.client.SendData = send_data
self.client_thread.client.handle_write()

#主程式
if __name__ == "__main__":
#建立和server連線, 並且等待溝通
client_thread = send_server_thread("localhost",111)
client_thread.start()
#等待user input
input_thread(client_thread).start()





參考資料 :
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/483732
http://www.chmhome.com/Python_Nutshell/index.html?page=0596001886_pythonian-chp-19-sect-3.html
http://effbot.org/librarybook/asyncore.htm

沒有留言: