天天看點

《Python核心程式設計(第3版)》——2.5 *SocketServer子產品

本節書摘來自異步社群《python核心程式設計(第3版)》一書中的第2章,第2.5節,作者[美] wesley chun(衛斯理 春),孫波翔 李斌 李晗 譯,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

socketserver是标準庫中的一個進階子產品(python 3.x中重命名為socketserver),它的目标是簡化很多樣闆代碼,它們是建立網絡用戶端和伺服器所必需的代碼。這個子產品中有為你建立的各種各樣的類,如表2-3所示。

通過複制前面展示的基本tcp示例,我們将建立一個tcp用戶端和伺服器。你會發現它們之間存在明顯的相似性,但是也應該看到我們如何處理一些繁瑣的工作,于是你不必擔心樣闆代碼。這些代表了你能夠編寫的最簡單的同步伺服器(為了将你的伺服器配置為異步運作,可以檢視本章末尾的練習)。

除了為你隐藏了實作細節之外,另一個不同之處是,我們現在使用類來編寫應用程式。因為以面向對象的方式處理事務有助于組織資料,以及邏輯性地将功能放在正确的地方。你還會注意到,應用程式現在是事件驅動的,這意味着隻有在系統中的事件發生時,它們才會工作。

《Python核心程式設計(第3版)》——2.5 *SocketServer子產品

事件包括消息的發送和接收。事實上,你會看到類定義隻包括一個用來接收用戶端消息的事件處理程式。所有其他的功能都來自使用的socketserver類。此外,gui程式設計(見第5章)也是事件驅動的。你會立即注意到它們的相似性,因為最後一行代碼通常是一個伺服器的無限循環,它等待并響應用戶端的服務請求。它工作起來幾乎與本章前面的基礎tcp伺服器中的無限while循環一樣。

在原始伺服器循環中,我們阻塞等待請求,當接收到請求時就對其提供服務,然後繼續等待。在此處的伺服器循環中,并非在伺服器中建立代碼,而是定義一個處理程式,這樣當伺服器接收到一個傳入的請求時,伺服器就可以調用你的函數。

在示例2-8中,首先導入伺服器類,然後定義與之前相同的主機常量。其次是請求處理程式類,最後啟動它。更多細節請檢視下面的代碼片段。

示例2-8 socketserver時間戳tcp伺服器(tstservss.py)

《Python核心程式設計(第3版)》——2.5 *SocketServer子產品

逐行解釋

第1~9行

最初的部分包括從socketserver導入正确的類。注意,這裡使用了python 2.4中引入的多行導入功能。如果使用的是較早版本的python,那麼将不得不使用完全限定的module.attribute名稱,或者在同一行中導入兩個屬性。

第11~15行

這裡進行了大量的工作。我們得到了請求處理程式myrequesthandler,作為socketserver中streamrequesthandler的一個子類,并重寫了它的handle()方法,該方法在基類request中預設情況下沒有任何行為。

當接收到一個來自用戶端的消息時,它就會調用handle()方法。而streamrequesthandler類将輸入和輸出套接字看作類似檔案的對象,是以我們将使用readline()來擷取用戶端消息,并利用write()将字元串發送回用戶端。

是以,在用戶端和伺服器代碼中,需要額外的回車和換行符。實際上,在代碼中你不會看到它,因為我們隻是重用那些來自用戶端的符号。除了這些細微的差别之外,它看起來就像以前的伺服器。

第17~19行

最後的代碼利用給定的主機資訊和請求處理類建立了tcp伺服器。然後,無限循環地等待并服務于用戶端請求。

如示例2-9所示,這裡的用戶端很自然地非常像最初的用戶端,比伺服器像得多,但必須稍微調整它以使其與新伺服器很好地工作。

示例2-9 socketserver時間戳tcp用戶端(tstclntss.py)

《Python核心程式設計(第3版)》——2.5 *SocketServer子產品

第1~8行

這裡沒有什麼特别之處,這是複制原來用戶端的代碼。

第10~21行

socketserver請求處理程式的預設行為是接受連接配接、擷取請求,然後關閉連接配接。由于這個原因,我們不能在應用程式整個執行過程中都保持連接配接,是以每次向伺服器發送消息時,都需要建立一個新的套接字。

這種行為使得tcp伺服器更像是一個udp伺服器。然而,通過重寫請求處理類中适當的方法就可以改變它。不過,我們将其留作本章末尾的一個練習。

除了用戶端現在有點“由内而外”(因為我們必須每次都建立一個連接配接)這個事實之外,其他一些小的差別已經在伺服器代碼的逐行解釋中給出:因為這裡使用的處理程式類對待套接字通信就像檔案一樣,是以必須發送行終止符(回車和換行符)。而伺服器隻是保留并重用這裡發送的終止符。當得到從伺服器傳回的消息時,用strip()函數對其進行處理并使用由print聲明自動提供的換行符。

這裡是socketserver tcp用戶端的輸出。

這是伺服器的輸出。

此時的輸出與最初的tcp用戶端和伺服器的輸出類似。然而,你應該會發現,我們連接配接了伺服器兩次。