應用Python快速實現系統原型
[摘要] 快速原型是在開發真實系統之前,構造一個原型,在該原型的基礎上,逐漸完成整個系統的開發工作。Python非常便於使用,並且支援Windows/Mac/Unix/Linux/Solaris多作業系統平台,可以協助開發人員在這些平台上更迅速地完成任務。同時Python擁有一個強大的基本類庫和數量眾多的第三方擴充庫,所以它的應用範圍也比較廣泛。文章舉例說明,如何應用Python快速實現一個非同步多線程的前置機系統。
[關鍵詞] 動態程式設計語言 快速原型開發 前置機 類比系統
一、系統背景
在開發跨平台業務系統的時候,為了確保後台伺服器的資料安全,用戶端通常是與交易前置機通訊,發送交易請求,交易前置機接收交易請求,然後與後台伺服器互動,從後台獲得該交易的返回資料後,再按照交易報文的格式返回給用戶端。
交易前置機所使用的作業系統情況較複雜,一般要根據客戶單位的具體情況,有些採用Windows系統,有些採用Unix/Linux或者Solaris系統。但在去客戶單位進行項目實施之前,開發人員需要在開發過程中隨時進行用戶端交易的調試工作,這就需要有個類比交易前置機的系統,在調試過程中能非常方便的修改交易報文的定義,類比後台返回的交易報文。在某些系統的開發中,這個類比系統就可以作為一個完整系統的原型,開發系統的過程,就是在此原型基礎上,逐漸完善交易類型與交易返回資料,最後完成整個系統的開發。
類比系統的開發可以用多種方法來實現,而動態語言Python便於使用、類庫豐富、修改方便。本文要闡述的是利用Python來進行類比系統的開發,快速實現交易前置機最常用的功能:接收交易請求、組織返回資料、發送返回資料。
二、 Python的異步(非阻塞)通訊
asyncore庫是python的一個標準庫,它是一個非同步socket的封裝。我們在進行網路通訊的時候可以直接使用socket等底層的庫,但是asyncore使得我們可以更加方便的進行網路通訊,避免直接使用socket,select,poll等工具時需要面對的複雜處理。
asyncore庫很簡單,只包含了一個loop()函數和一個dispatcher基類。每一個從dispatcher繼承的類的對象,都可以看作我們需要處理的一個socket,可以是TCP串連或者UDP。
asyncore庫使用容易,我們只需要定義一個類,它繼承dispatcher,然後我們重寫(覆蓋)一些方法就可以了。我們需要重寫的方法一般都以handle_打頭的。
loop()函數負責檢測一個字典,字典中儲存dispatcher的執行個體,這個字典被稱為channel。每次建立一個dispatcher對象,都會把自己加入到一個預設的字典裡面去。當對象被加入到channel中的時候,socket的行為都已經被定義好,程式只需要調用loop(),非同步多線程通訊功能就準備好了。
三、 專註於業務處理
在我們的類比系統中,由於需要同時為多個用戶端服務,定義了一個主通訊服務類,繼承自asyncore.dispatcher類,重寫該類的handle_accept方法,從這裡獲得用戶端的串連,向控制台輸出用戶端IP與連接埠資訊,並轉寄給處理線程類。
處理線程類繼承自asyncore.dispatcher_with_send,此類可以接收和發送資料,只需要重寫handle_read方法即可。
這樣,一個非同步多線程的交易類比系統就可以運行了。
每當用戶端有資料發送過來,就會觸發handle_read方法。在此方法中,可以對交易資料進行合法性判斷、提取交易類別、提取交易資料、組織交易返回資料、發送資料到用戶端,通過這一系列的處理,一個交易的完整過程就完成了。
在這裡,通過對交易類別的判斷,可以很容易的增加新的交易。
四、 代碼摘要
1.匯入庫檔案
import asyncore import socketimport sys
2.主通訊服務類
類初始化時,建立socket串連,綁定在指定的連接埠上,並且可以設定最大的並發串連數,此處預設為10,最多可以同時為10個用戶端服務。
當用戶端呼叫過來後,觸發handle_accept方法,這裡輸出用戶端IP地址和連接埠資訊,啟動處理線程類SecondaryServerSocket。
用戶端中斷連線時,觸發handle_colse方法,此方法也調用asyncore.dispatcher類的close方法,做一些善後處理工作。
class MainServerSocket(asyncore.dispatcher):def __init__(self, port):asyncore.dispatcher.__init__(self)self.create_socket(socket.AF_INET, socket.SOCK_STREAM)self.bind(('',port))#可以通過設定檔來設定最大串連數,目前是10個串連self.listen(10) #最大可以同時為N個用戶端服務def handle_accept(self):newSocket, address = self.accept( )print( "新的串連,來自{}".format(address) )SecondaryServerSocket(newSocket)def handle_close(self):asyncore.dispatcher.close(self)
3.處理線程類
此類繼承自asyncore.dispatcher_with_send,當用戶端發送交易報文過來時,觸發handle_read方法。
在handle_read方法中,根據具體的通訊協議,分析交易報文,然後針對不同的交易類型,分別進行處理。最後,處理好的交易報文,下發給對應的用戶端。
class SecondaryServerSocket(asyncore.dispatcher_with_send):def handle_read(self):receivedHead = self.recv(4)if receivedHead:if (len(receivedHead)==4) and (receivedHead.isdigit() ):print( "報文頭接收成功,報文長度:{}...".format(receivedHead) )receivedData = self.recv(int(receivedHead))print( "指定長度報文內容接收成功..." )#----------------------#0.特殊交易處理:伺服器退出#----------------------if ( len(receivedData)==4 ) and ( receivedData.lower().endswith("exit") ) :self.close()print( "接收到退出命令,伺服器退出!" )try:asyncore.close_all()returnexcept:sys.exit(0)return#----------------------#...正常交易的處理#----------------------。。。else:self.close()print( "接收到非法報文頭!" )for x in range(len(receivedHead)):print hex(ord(receivedHead[x])) , " ",print()else: self.close( )def handle_close(self):print( "中斷連線,來自{}.".format( self.getpeername( ) ) )
4.主程式入口
程式從這裡啟動,首先列印程式協助資訊,然後在指定的偵聽連接埠上,啟動主通訊服務線程。
if __name__ == "__main__" :print __doc__MainServerSocket(3555)print( "伺服器開始偵聽..." )asyncore.loop( )
[參考資料]
1、《Python入門指南》Release2.5b2,作者:Guido van Rossum,2006.07.11,Python中國使用者論壇集體翻譯。
2、《Python核心編程》第二版,作者:(美)丘恩(Chun,W.J.),翻譯:宋吉廣,2008.07,北京:人民郵電出版社。
3、《深入Python :Dive Into Python中文版》5.4b,作者:MarkPilgrim, Python中國使用者論壇集體翻譯,Updated 審校 (5.4b):2007 年6 月—9 月