陳碩 (giantchen_AT_gmail)
Blog.csdn.net/Solstice
本文将介紹第一個示例:五個簡單 TCP 網絡服務協定,包括 echo (RFC 862)、discard (RFC 863)、chargen (RFC 864)、daytime (RFC 867)、time (RFC 868),以及 time 協定的用戶端。各協定的功能簡介如下:
discard - 丢棄所有收到的資料;
daytime - 服務端 accept 連接配接之後,以字元串形式發送目前時間,然後主動斷開連接配接;
time - 服務端 accept 連接配接之後,以二進制形式發送目前時間(從 Epoch 到現在的秒數),然後主動斷開連接配接;我們需要一個客戶程式來把收到的時間轉換為字元串。
echo - 回顯服務,把收到的資料發回用戶端;
chargen - 服務端 accept 連接配接之後,不停地發送測試資料。
剩下的都是例行公事的代碼,為節省篇幅,此處從略,請閱讀 muduo/examples/simple/daytime。
用 netcat 扮演用戶端,運作結果如下:
$ nc 127.0.0.1 2013
2011-02-02 03:31:26.622647 # 伺服器傳回的時間字元串
剩下的都是例行公事的代碼,為節省篇幅,此處從略,請閱讀 muduo/examples/simple/time。
用 netcat 扮演用戶端,并用 hexdump 來列印二進制資料,運作結果如下:
$ nc 127.0.0.1 2037 | hexdump -C
00000000 4d 48 d0 d5 |MHÐÕ|
00000004
注意其中考慮到了如果資料沒有一次性收全,已經收到的資料會暫存在 Buffer 裡,以等待下一次機會,程式也不會阻塞。這樣即便伺服器一個位元組一個位元組地發送資料,代碼還是能正常工作,這也是非阻塞網絡程式設計必須在使用者态使用接受緩沖的主要原因。
這是我們第一次用到 TcpClient class,完整的代碼如下:
程式的運作結果如下,假設 time server 運作在本機:
$ ./simple_timeclient 127.0.0.1
2011-02-02 04:10:35.181717 4296 INFO pid = 4296 - timeclient.cc:71
2011-02-02 04:10:35.183668 4296 INFO TcpClient::connect[TimeClient] - connecting to 127.0.0.1:2037 - TcpClient.cc:60
2011-02-02 04:10:35.185178 4296 INFO 127.0.0.1:40960 -> 127.0.0.1:2037 is UP - timeclient.cc:39
2011-02-02 04:10:35.185279 4296 INFO Server time = 1296619835, 2011-02-02 04:10:35.000000 - timeclient.cc:56
2011-02-02 04:10:35.185354 4296 INFO 127.0.0.1:40960 -> 127.0.0.1:2037 is DOWN - timeclient.cc:39
這段代碼實作的不是行回顯(line echo)服務,而是有一點資料就發送一點資料。這樣可以避免用戶端惡意地不發送換行字元,而服務端又必須緩存已經收到的資料,導緻伺服器記憶體暴漲。但這個程式還是有一個安全漏洞,即如果用戶端故意不斷發生資料,但從不接收,那麼服務端的發送緩沖區會一直堆積,導緻記憶體暴漲。解決辦法可以參考下面的 chargen 協定。
剩下的都是例行公事的代碼,為節省篇幅,此處從略,請閱讀 muduo/examples/simple/echo。
練習 1:修改 EchoServer::onMessage(),實作大小寫互換。
練習 2:修改 EchoServer::onMessage(),實作 rot13 加密。
剩下的都是例行公事的代碼,為節省篇幅,此處從略,請閱讀 muduo/examples/simple/chargen。
完整的 chargen 服務端還帶流量統計功能,用到了定時器,我們會在下一篇文章裡介紹定時器的使用,到時候再回頭來看相關代碼。
$ nc localhost 2019 | head
!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefgh
"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghi
#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghij
$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijk
%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijkl
&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklm
'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmn
()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmno
)*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnop
*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopq
前面五個程式都用到了 EventLoop,這其實是個 Reactor,用于注冊和分發 IO 事件。Muduo 遵循 one loop per thread 模型,多個服務端(TcpServer)和用戶端(TcpClient)可以共享同一個 EventLoop,也可以配置設定到多個 EventLoop 上以發揮多核多線程的好處。這裡我們把五個服務端用同一個 EventLoop 跑起來,程式還是單線程的,功能卻強大了很多:
以上幾個協定的消息格式都非常簡單,沒有涉及 TCP 網絡程式設計中常見的分包處理,在下一篇文章講 Boost.Asio 的聊天伺服器時我們再來讨論這個問題。
(待續)
本文轉自 陳碩 部落格園部落格,原文連結:http://www.cnblogs.com/Solstice/archive/2011/02/02/1948839.html,如需轉載請自行聯系原作者