HP-Socket
以前為某大型通訊項目開發了一套通用 Windows Socket TCP 底層通訊組件,組件代號為 HP-Socket。現在把 HP-Socket 的所有代碼向福士公開,希望能對大家有所協助;另外,為了讓大家能更方便的學習 HP-Socket,因此精心製作了一個功能測試樣本(Test Echo)和一個效能測試樣本(Test Echo-PFM),大家可以通過這兩個測試樣本入手,迅速掌握組件的設計思想和使用方法。HP-Socket 包含服務端組件(IOCP 模型)和用戶端組件(Event Select 模型),具備以下重要特點:
通用性
- 通訊組件的唯一職責就是接受和發送位元組流,絕對不能參與上層協議解析等工作;
- 與上層使用者解耦、互不依賴,組件與使用者通過操作介面和監聽器介面進行互動,組件實現操作介面為上層提供操作方法;使用者實現監聽器介面把自己註冊為組件的 Listener,接收組件通知。因此,任何使用者只要實現了監聽器介面都可以使用組件;另一方面,甚至可以自己重新寫一個實現方式完全不同的組件實現給使用者調用,只要該組件遵從組件的操作介面,這也是 DIP 設計原則的體現。
可用性
可用性對所有萬用群組件都是至關重要的,如果太難用還不如自己重頭寫一個來得方便。因此,組件的操作介面和監聽器介面設計得盡量簡單易用(通俗來說就是“傻瓜化”),這兩個介面的主要方法均不超過 5 個。另外,組件完全封裝了所有的底層 Socket 通訊,上層應用看不到任何通訊細節,不必也不能干預任何通訊操作,Socket 串連被抽象為 Connection ID,該參數作為串連標識提供給上層應用識別不同的串連。
高效能
作為底層的萬用群組件,效能問題是必須考慮的,絕對不能成為系統的瓶頸。而另一方面,從實際出發,根據用戶端組件與服務端組件的效能要求採用不同的 Socket 模型。組件在設計上充分考慮了效能、現實使用情景、可用性和實現複雜性等因素,確保滿足效能要求的同時又不會寫得太複雜。做出以下兩點設計決策:
- 用戶端:在單獨線程中實現 Socket 通訊互動。這樣可以避免與主線程或其他線程相互幹擾;I/O 模型選擇 Event Select 通訊模型。
- 服務端:採用 Windows 平台效率最高的 IOCP 通訊模型;利用緩衝池技術,在通訊的過程中,通常需要頻繁的申請和釋放記憶體緩衝區,建立了動態緩衝池, 只有當緩衝池中沒有可用對象時才建立新對象,而當緩衝對象過多時則會壓縮緩衝池;另外,組件的動態記憶體通過私人堆(Private Heap)機制分配,避免與 new / malloc 競爭同時又減少記憶體空洞。
伸縮性
可以根據實際的使用環境要求設定組件的各項績效參數(如:背景工作執行緒的數量、各種緩衝池的大小、收發緩衝區的大小、Socket 監聽隊列的大小、Accep 派發的數目以及心跳檢查的間隔等)。
HP-Socket 源碼、樣本及文檔資源:請訪問項目首頁 ^_*
v2.0.1 更新:
=============================================
> Server:
-----------------
1、IServerSocketListener 增加 OnPrepareSocket(connID, socket) 通知方法用於在使用 socket 前設定 SOCKET 選項或過濾用戶端串連
2、ISocketServer 增加方法 Disconnect(connID) 用於主動斷開用戶端串連
3、增加 IServerSocketListener 的子類 CServerSocketListener,提供預設(空的)通知處理方法
> Client:
-----------------
1、IClientSocketListener 增加 OnPrepareSocket(connID, socket) 通知方法用於在使用 socket 前設定 SOCKET 選項
2、支援非同步 Connect:ISocketServer 的 Start() 方法增加一個參數 (BOOL bAsyncConnect) 設定是否採用非同步 Connect
3、增加 IClientSocketListener 的子類 CClientSocketListener,提供預設(空的)通知處理方法。
4、修複BUG:超高負載情形下出現丟包現象
> 其它更新:
-----------------
1、支援 Windows x64 平台
2、最佳化 TestEcho 和 TestEcho-PFM 測試程式3、TestEcho 用戶端程式加入“非同步串連”樣本4、TestEcho 服務端程式加入“串連過濾”和“主動中斷連線”樣本
原文:《高效能 Windows Socket 服務端與用戶端組件(原始碼及測試案例下載)》
自從本座發表了兩篇關於 Windows Socket 通訊組件實現的文章後,收到不少讀者的留言,希望能分享完整的原始碼。此時,本座不敢弊帚自珍。特意在此提供服務端組件和用戶端組件的完整代碼。另外,為便於讀者學習和理解,花了一點點時間精心製作了兩個測試案例,一個用於功能測試(TestEcho),另一個用於效能測試(TestEcho-PFM)。讀者可以通過這兩個測試案例入手,迅速掌握組件的使用方法。希望對大家有所協助,謝謝 ~ ^_^ ~
(輕踩這裡,你懂的 ^_*)
原文:《基於 IOCP 的通用非同步 Windows Socket TCP 高效能服務端組件的設計與實現》
設計概述
服務端通訊組件的設計是一項非常嚴謹的工作,其中效能、伸縮性和穩定性是必須考慮的硬性品質指標,若要把組件設計為萬用群組件提供給多種已知或未知的上層應用使用,則設計的難度更會大大增加,通用性、可用性和靈活性必須考慮在內。
現以一個基於 IOCP 的通用非同步 Windows Socket TCP 服務端組件為例子,講述其設計與實現相關的問題,希望能引發大家的思考,對大家日後開展相關類似工作時有所協助。關於通用性、可用性、Socket 模型選型以及介面模型的設計等問題已經在本座前段時間發表的《通用非同步 Windows Socket TCP 用戶端組件的設計與實現》中進行過闡述,此處就不再重複了。現在主要針對服務端通訊組件的特點闡述設計其設計和實現相關的問題。
一、線程結構
與組件相關的線程有 3 種:使用者線程、Accept 線程和背景工作執行緒,其中後 2 種由組件實現。
-
- 使用者線程:通過調用 Start/Stop/Send 等組件方法操作組件的一個或多個線程,通常是程式的主線程或其它商務邏輯線程。
- Accept 線程:使用 AcceptEx() 接收用戶端串連請求並建立 Client Socket 的線程,將其獨立出來,實現為單獨的線程將使組件的模組劃分更清晰,更重要的是避免與商務邏輯和通訊處理的相互影響。
- 背景工作執行緒:使用 GetQueuedCompletionStatus() 監聽網路事件並處理網路互動的多個線程,背景工作執行緒處理完網路事件後會向上層應用發送 OnAccept/OnSend/OnReceive 等組件通知。背景工作執行緒的數量可以根據實際情況之行設定(通常建議為:CPU Core Number * 2 + 2)。
注意:如果上層應用在接收到 OnAccept/OnSend/OnReceive 這些組件通知時直接進行商務邏輯處理並在其中操作組件,則背景工作執行緒也成為了使用者線程。另外,如果要處理的商務邏輯比較耗時,上層應用應該在接收到組件通知後交由其他線程處理。
二、效能
組件採用 Windows 平台效率最高的 IOCP Socket 通訊模型,因此在通訊介面的效能方面是有保證的,這裡就不多說了。現在從組件的設計與實現的角度來來闡述效能的最佳化。組件在代碼層級做了很多最佳化,一些看似多餘或繁瑣的代碼其實都是為了效能服務;組件在設計方面主要採用了 2 中最佳化策略:緩衝池和私人堆。
- 緩衝池:在通訊的過程中,通常需要頻繁的申請和釋放記憶體緩衝區(TBufferObj)和 Socket 相關的結構體(TSocketObj),這會大大影響組件的效能,因此,組件為 TBufferObj 和 TSocketObj 建立了動態緩衝池, 只有當緩衝池中沒有可用對象時才建立新對象,而當緩衝對象過多時則會壓縮緩衝池。
- 私人堆(Private Heap):在作業系統中,new / malloc 等操作是序列化的,雖然一般的應用程式不用太在乎這個問題,但是在一個高並發的伺服器中則是個不可忽略的問題,另外 TBufferObj 和 TSocketObj 均為大小固定的結構體,因此非常適合在私人堆中分配記憶體,避免與 new / malloc 競爭同時又減少記憶體空洞。(關於私人堆的使用方法請參考這裡 ^_^)
三、通用性與可用性
與《通用非同步 Windows Socket TCP 用戶端組件的設計與實現》描述的用戶端介面一樣,服務端組件也提供了兩組介面:ISocketServer 介面提供組件操作方法,由上層應用直接調用;IServerSocketListener 介面提供組件通知方法,由上層應用實現,這兩個介面設計得非常簡單,主要方法均不超過 5 個。由於組件自身功能完備(不需要附帶其它庫或代碼)並且職責單一(只管通訊,不參與商務邏輯),因此可以十分方便第整合到任何類型的應用程式中。
四、伸縮性
可以根據實際的使用環境要求設定背景工作執行緒的數量、 TBufferObj 和 TSocketObj 緩衝池的大小、TBufferObj 緩衝區的大小、Socket 監聽隊列的大小、AccepEx 派發的數目以及心跳檢查的間隔等。
五、串連標識
組件完全封裝了所有的底層 Socket 通訊,上層應用看不到任何通訊細節,不必也不能干預任何通訊操作。另外,組件在 IServerSocketListener 通知介面的所有方法中都有一個 Connection ID 參數,該參數作為串連標識提供給上層應用識別不同的串連。
原文:《通用非同步 Windows Socket TCP 用戶端組件的設計與實現》
設計概述
編寫 Windows Socket TCP 用戶端其實並不困難,Windows 提供了6種 I/O 通訊模型供大家選擇。但本座看過很多用戶端程式都把 Socket 通訊和商務邏輯混在一起,剪不斷理還亂。每個程式都 Copy / Parse 類似的代碼再進行修改,實在有點情何以堪。因此本座利用一些閑暇時光寫了一個基於 IOCP 的通用非同步 Windows Socket TCP 高效能服務端組件和一個通用非同步 Windows Socket TCP 用戶端組件供各位看官參詳參詳,希望能激發下大家的靈感。本篇文章講述用戶端組件。閑話少說,我們現在步入正題。
答:很簡單。
1、限制組件的職能,說白了,通訊組件的唯一職責就是接受和發送位元組流,絕對不能參與上層協議解析等工作。不在其位不謀其政就是這個意思。
2、與上層使用者解耦、互不依賴,組件與使用者通過介面方法進行互動,組件實現 ISocketClient 介面為上層提供操作方法;使用者通過 IClientSocketListener 介面把自己註冊為組件的 Listener,接收組件通知。因此,任何使用者只要實現了 IClientSocketListener 介面都可以使用組件;另一方面,你甚至可以自己重新寫一個實現方式完全不同的組件實現給使用者調用,只要該組件遵從 ISocketClient 介面。這也是 DIP 設計原則的體現(若想瞭解更多關於設計原則的內容請猛擊這裡 ^_^)。
- 最重要的第二個問題:可用性如何,也就是說使用起來是否是否方便?
答:這個問題問得很好,可用性對所有萬用群組件都是至關重要的,如果太難用還不如自己重頭寫一個來得方便。因此,ISocketClient 和 IClientSocketListener 介面設計得盡量簡單易用(通俗來說就是“傻瓜化”),這兩個介面的主要方法均不超過 5 個。
作為底層的萬用群組件,效能問題是必須考慮的,絕對不能成為系統的瓶頸。而另一方面,從實際出發,畢竟只是一個用戶端組件,它的並發性要求遠沒有服務端那麼高。因此,組件在設計上充分考慮了效能、現實使用情景、可用性和實現複雜性等因素,確保滿足效能要求的同時又不會寫得太複雜。做出以下兩點設計決策:
- 在單獨線程中實現 Socket 通訊互動。這樣可以避免與主線程或其他線程相互幹擾。
- I/O 模型選擇 WSAEventSelect。細說一下選擇這種 I/O 模型的原因:(各種 I/O 模型的效能比較可以參考:《Windows 網路編程(中文第二版)》第 154 頁)
- 阻塞模型:(不解析,你懂的^_^)
- 非阻塞模型:(效能太低)
- WSAAsyncSelect: (兩個原因:a、效能太低;b、對於純 Console 程式還要背負 HWND 實在是傷不起呀!)
- 重疊 I/O:(有點複雜了)
- 完成連接埠:(何必呢?)
CodeProject