跨平台網絡庫(采用C++ 11)


I:跨平台設計基礎

  在windows下使用0字節的WSARecv/WSASend(讀寫)作為讀寫檢測,將IOCP作為一個通知模型,而"拋棄"它的異步模型。

 即:把它當作epoll來用。使得(方便)網絡庫的設計(譬如socket的讀寫處理)在windows和linux下實現統一:  底層獲得讀寫通知,應用層(網絡庫中)自己處理讀寫。

II:單線程EventLoop  

  1:EventLoop是一個單線程的網絡IO循環,使用一個iocp(epoll)實例,管理多個DataSocket。  

     通過EventLoop的addConnection接口為它添加DataSocket(並傳入一個回調),並會在EventLoop接手它后觸發傳入的回調函數--處理鏈接建立之后的業務邏輯。  

     DataSocket 即為一個客戶端連接-會話,用戶可以為它設置事件回調函數。  

       setDataHandle:設置收到消息后的回調函數。  

       setDisConnectHandle:設置鏈接斷開后的回調函數。

     當然我們也可以通過disConnect主動斷開會話的網絡連接。 

  2:EventLoop實現線程安全的wakeup和異步消息隊列,用於外部邏輯線程投遞異步操作(以及喚醒,以讓eventloop盡可能更快的處理請求)。

  3:std::shared_ptr作為packet類型,用於將同一個packet投遞給多個客戶端DataSocket(避免分配多個消息),DataSocket 內置一個隊列,保存當前pending在它上的待發packet list。  

     之所以采用std::shared_ptr,是因為它自帶引用計數處理,可以方便的應對同一個消息包發送給多個客戶端時,消息包的分配和釋放問題(即此消息全部發送給它的目的客戶端,那么此消息就可以回收),

    無需自己再寫一套消息包設計了。

  注:目前linux下給客戶端flush發送網絡消息時,采用writev提高效率,但windows上沒有找到相關函數(WSASend不滿足要求),但可以在某些時候將消息memcpy到一個緩沖區,然后一次性send(以減少send系統調用次數))。

III: 封裝EventLoop的TCPServer  

  1:TCPServer處理Listen邏輯以及為新到的鏈接分配(通過EventLoop的addConnection接口)一個EventLoop。

  2!:可從源碼中看到DataSocket的事件回調函數所附帶的參數是DataSocket*, 但是 TcpServer回調函數參數中,表示會話標識的類型為:int64 id,而非裸指針。  

       這是因為TCPServer多被用於多線程設計,此時會話的有效性(避免串話)(以及內存的有效性-野指針問題)需要保證,而裸指針並非安全的。

  3:第二點說到TcpServer多用於多線程設計時,具體如下:

    在它的回調函數中,我們可以將消息投遞到一個邏輯線程的消息隊列,並wakeup邏輯線程的EventLoop),當邏輯線程被喚醒后,從消息隊列中同步讀取消息,然后處理。  

   而當邏輯線程需要發送消息則使用: TCPServer的send接口,參數是一個int64_t id 表示要發送消息的會話,緊接着是一個Packet,表示消息內容。  

  當然!:TCPServer的回調函數中可以立即處理,而非投遞到別的線程進行協作,這樣用起來當然更簡單了。

  譬如在某些網絡服務中,不需要很耗時的處理,而僅僅是IO密集型(比如網關),  那么建議直接在回調函數中進行處理(譬如轉發)。

 

注: 此網絡庫參考了 muduo:https://github.com/chenshuo/muduo

另外致謝:sniperhw:http://www.cnblogs.com/sniperhw 近幾年的指點

網絡庫代碼地址: https://github.com/IronsDu/accumulation-dev/tree/master/cpp_net

 目前main.cpp實現的是一個ping pong測試(一個進程內:服務器用TCPServer,用多個客戶端線程跑各自的EventLoop)。

  從main.cpp也可以看到 單線程EventLoop和TCPServer多線程加消息隊列的使用方式。

(VS版本至少 VS2013),Linux下簽出整個項目后進入/examples/DNet/DNet目錄, (我用的g++ 4.9版本 )再使用命令:

g++ -I./../../../cpp_common -I./../../../common eventloop.cpp datasocket.cpp TCPServer.cpp main.cpp -std=c++0x -lrt

 

TODO::代碼中有一些TODO,表示晦澀或者我不太確定沒問題~HOHO

另外我很期待后面要做的廣播測試(類似MMO的AOI,多個玩家同時移動,要廣播給周圍N個玩家)。

 

目前ping pong測試(客戶端和服務器在同一個進程內,編譯時不開任何優化)在我的機器(AMD Athlon(tm) 7750 Dual-Core Processor,1.3G Hz, CentOS 6.3):

1:TCPServer使用一個線程,並將收到的消息投遞到邏輯線程進行ping pong處理。

100個鏈接,消息包大小為4K,每秒吞吐為190M/s。

1000個鏈接,消息包大小為4K,每秒吞吐為135M/s。

10000個鏈接,消息包大小為4K,每秒吞吐為125M/s。

2:TCPServer使用一個線程,直接在自身的消息回調函數中進行ping pong處理。

100個鏈接,消息包大小為4K,每秒吞吐為315M/s。

1000個鏈接,消息包大小為4K,每秒吞吐為190M/s。

10000個鏈接,消息包大小為4K,每秒吞吐為160M/s。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM