發布一個基於 Reactor 模式的 C++ 網路程式庫

來源:互聯網
上載者:User

發布一個基於 Reactor 模式的 C++ 網路程式庫

陳碩 (giantchen_AT_gmail)

Blog.csdn.net/Solstice

2010 Aug 30

本文主要介紹 muduo 網路程式庫的使用。其設計與實現將有另文講解。

目錄

由來 1

下載與編譯 2

例子 2

基本結構 3

公開介面 4

內部實現 4

執行緒模式 5

結語 5

由來

半年前我寫了一篇《學之者生,用之者死——ACE曆史與簡評》,其中提到“我心目中理想的網路程式庫”的樣子:

  • 安全執行緒,支援多核多線程
  • 不考慮可移植性,不跨平台,只支援 Linux,不支援 Windows。
  • 在不增加複雜度的前提下可以支援 FreeBSD/Darwin,方便將來用 Mac 作為開發用機,但不為它做效能最佳化。也就是說 IO multiplexing 使用 poll 和 epoll。
  • 主要支援 x86-64,兼顧 IA32
  • 不支援 UDP,只支援 TCP
  • 不支援 IPv6,只支援 IPv4
  • 不考慮廣域網路應用,只考慮區域網路
  • 只支援一種使用模式:non-blocking IO + one event loop per thread,不考慮阻塞 IO
  • API 簡單易用,只暴露具體類和標準庫裡的類,不使用 non-trivial templates,也不使用虛函數
  • 只滿足常用需求的 90%,不面面俱到,必要的時候以 app 來適應 lib
  • 只做 library,不做成 framework
  • 爭取全部代碼在 5000 行以內(不含測試)
  • 以上條件都滿足時,可以考慮搭配 Google Protocol Buffers RPC

在想清楚這些目標之後,我開始第三次嘗試編寫自己的 C++ 網路程式庫。與前兩次不同,這次我一開始就想好了庫的名字,叫 muduo (木鐸),並在 Google code 上建立了項目: http://code.google.com/p/muduo/ 。muduo 的主體內容在 5 月底已經基本完成,現在我把它開源。

本文主要介紹 muduo 網路程式庫的使用,其設計與實現將有另文講解。

下載與編譯

: http://muduo.googlecode.com/files/muduo-0.1.0-alpha.tar.gz

SHA1 Checksum: 5d3642e311177ded89ed0d15c10921738f8c984c

Muduo 使用了 Linux 較新的系統調用,要求 Linux 的核心版本大於 2.6.28 (我自己用的是 2.6.32 )。在 Debian Squeeze / Ubuntu 10.04 LTS 上編譯測試通過,32 位和 64 位元系統都能使用。

Muduo 採用 CMake 為 build system,安裝方法:

$ sudo apt-get install cmake

Muduo 依賴 Boost,很容易安裝:

$ sudo apt-get install libboost1.40-dev # 或 libboost1.42-dev

編譯方法很簡單:

$ tar zxf muduo-0.1.0-alpha.tar.gz

$ cd muduo/

$ ./build.sh

# 編譯產生的可執行檔和靜態庫檔案分別位於 ../build/debug/{bin,lib}

如果要編譯 release 版,可執行

$ BUILD_TYPE=release ./build.sh

# 編譯產生的可執行檔和靜態庫檔案分別位於 ../build/release/{bin,lib}

編譯完成之後請試運行其中的例子。比如 bin/inspector_test ,然後通過瀏覽器訪問 http://10.0.0.10:12345/ 或 http://10.0.0.10:12345/proc/status,其中 10.0.0.10 替換為你的 Linux box 的 IP。

例子

Muduo 附帶了幾十個小例子,位於 examples 目錄。其中包括從 Boost.Asio、JBoss Netty、Python Twisted 等處移植過來的例子。

examples

|-- simple # 簡單網路通訊協定的實現

|   |-- allinone  # 在一個程式裡同時實現下面 5 個協議

|   |-- chargen   # RFC 864,可測試頻寬

|   |-- daytime # RFC 867

|   |-- discard # RFC 863

|   |-- echo # RFC 862

|   |-- time # RFC 868

|   `-- timeclient # time 協議的用戶端

|-- hub # 一個簡單的 pub/sub/hub 服務,示範應用級的廣播

|-- roundtrip # 測試兩台機器的網路延時與時間差

|-- asio # 從 Boost.Asio 移植的例子

|   |-- chat # 聊天服務

|   `-- tutorial # 一系列 timers

|-- netty # 從 JBoss Netty 移植的例子

|   |-- discard # 可用於測試頻寬,伺服器可多線程運行

|   |-- echo # 可用於測試頻寬,伺服器可多線程運行

|   `-- uptime # TCP 長串連

`-- twisted # 從 Python Twisted 移植的例子

    `-- finger # finger01 ~ 07

基本結構

Muduo 的目錄結構如下。

muduo

|-- base # 與網路無關的基礎代碼,已提前發布

`-- net # 網路程式庫

    |-- http # 一個簡單的可嵌入的 網頁伺服器

    |-- inspect # 基於以上 網頁伺服器的“窺探器”,用於報告進程的狀態

    `-- poller # poll(2) 和 epoll(4) 兩種 IO multiplexing 後端

Muduo 是基於 Reactor 模式的網路程式庫,其核心是個事件迴圈 EventLoop,用於響應計時器和 IO 事件。Muduo 採用基於對象(object based)而非物件導向(object oriented)的設計風格,其介面多以 boost::function + boost::bind 表達。

Muduo 的標頭檔明確分為客戶可見和客戶不可見兩類。客戶可見的為白底,客戶不可見的為灰底。

這裡簡單介紹各個標頭檔及 class 的作用,詳細的介紹留給以後的部落格。

公開介面
  • Buffer 仿 Netty ChannelBuffer 的 buffer class,資料的讀寫透過 buffer 進行
  • InetAddress 封裝 IPv4 地址 (end point),注意,muduo 目前不能解析網域名稱,只認 IP
  • EventLoop 反應器 Reactor,使用者可以註冊計時器回調
  • EventLoopThread 啟動一個線程,在其中運行 EventLoop::loop()
  • TcpConnection 整個網路程式庫的核心,封裝一次 TCP 串連
  • TcpClient 用於編寫網路用戶端,能發起串連,並且有重試功能
  • TcpServer 用於編寫網路伺服器,接受客戶的串連
  • 在這些類中,TcpConnection 的生命期依靠 shared_ptr 控制(即使用者和庫共同控制)。Buffer 的生命期由 TcpConnection 控制。其餘類的生命期由使用者控制。
  • HttpServer 和 Inspector,暴露出一個 http 介面,用於監控進程的狀態,類似於 Java JMX。這麼做的原因是,《程式員修鍊之道》第 6 章第 34 條提到“對於更大、更複雜的伺服器代碼,提供其操作的內部試圖的一種漂亮技術是使用內建的 Web 服務器”,Jeff Dean 也說“(每個 Google 的伺服器處理序)Export HTML-based status pages for easy diagnosis”。
內部實現
  • Channel 是 selectable IO channel,負責註冊與響應 IO 事件,它不擁有 file descriptor。它是 Acceptor、Connector、EventLoop、TimerQueue、TcpConnection 的成員,生命期由後者控制。
  • Socket 封裝一個 file descriptor,並在析構時關閉 fd。它是 Acceptor、TcpConnection 的成員,生命期由後者控制。EventLoop、TimerQueue 也擁有 fd,但是不封裝為 Socket。
  • SocketsOps 封裝各種 sockets 系統調用。
  • EventLoop 封裝事件迴圈,也是事件指派的中心。它用 eventfd(2) 來非同步喚醒,這有別於傳統的用一對 pipe(2) 的辦法。它用 TimerQueue 作為計時器管理,用 Poller 作為 IO Multiplexing。
  • Poller 是 PollPoller 和 EPollPoller 的基類,採用“電平觸發”的語意。它是 EventLoop 的成員,生命期由後者控制。
  • PollPoller 和 EPollPoller 封裝 poll(2) 和 epoll(4) 兩種 IO Multiplexing 後端。Poll 的存在價值是便於調試,因為 poll(2) 調用是上下文無關的,用 strace 很容易知道庫的行為是否正確。
  • Connector 用於發起 TCP 串連,它是 TcpClient 的成員,生命期由後者控制。
  • Acceptor 用於接受 TCP 串連,它是 TcpServer 的成員,生命期由後者控制。
  • TimerQueue 用 timerfd 實現定時,這有別於傳統的設定 poll/epoll_wait 的等待時間長度的辦法。為了簡單起見,目前用鏈表來管理 Timer,如果有必要可改為優先隊列,這樣複雜度可從 O(n) 降為 O(ln n) (某些操作甚至是 O(1))。它是 EventLoop 的成員,生命期由後者控制。
  • EventLoopThreadPool 用於建立 IO 線程池,也就是說把 TcpConnection 指派到一組運行 EventLoop 的線程上。它是 TcpServer 的成員,生命期由後者控制。
執行緒模式

Muduo 的執行緒模式符合我主張的 one loop per thread + thread pool 模型。每個線程最多有一個 EventLoop。每個 TcpConnection 必須歸某個 EventLoop 管理,所有的 IO 會轉移到這個線程,換句話說一個 file descriptor 只能由一個線程讀寫。TcpConnection 所在的線程由其所屬的 EventLoop 決定,這樣我們可以很方便地把不同的 TCP 串連放到不同的線程去,也可以把一些 TCP 串連放到一個線程裡。TcpConnection 和 EventLoop 是安全執行緒的,可以跨線程調用。TcpServer 直接支援多線程,它有兩種模式:

1. 單線程,accept 與 TcpConnection 用同一個線程做 IO。

2. 多線程,accept 與 EventLoop 在同一個線程,另外建立一個 EventLoopThreadPool,新到的串連會按 round-robin 方式分配到線程池中。

結語

Muduo 是我對常見網路編程任務的總結,用它我能很容易地編寫多線程的 TCP 伺服器和用戶端。Muduo 是我業餘時間的作品,代碼估計還有很多 bug,功能也不完善(例如不支援 signal 處理),待日後慢慢改進吧。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.