剖析Etwork網(wǎng)絡(luò)庫
Author : Kevin Lynx
從開始接觸網(wǎng)絡(luò)編程這個東西開始,我就不間斷地閱讀一些網(wǎng)絡(luò)庫(模塊)的源代碼,主要目的是為了獲取別
人在這方面的經(jīng)驗,編程這東西,還是要多實踐啊。
基本上,Etwork是一個很小巧的網(wǎng)絡(luò)庫。Etwork基于select模型,采用我之前說的技巧,理論上可以處理很
多連接(先不說效率)。
先看看下這個庫的結(jié)構(gòu):
如同很多網(wǎng)絡(luò)庫一樣,總會有一個類似于ISocketManager的類,用于管理所有網(wǎng)絡(luò)連接(當(dāng)用戶服務(wù)器時)。
而ISocket則用于代表一個網(wǎng)絡(luò)連接。在其他庫中,ISocketManager對應(yīng)的可能就是Server,而ISocket對應(yīng)
的則是Session。
在接口設(shè)計上,盡管Etwork寫了很多接口類(看看那些IClass),但是事實上它抽象得并不徹底。只是暴露給
客戶端的代碼很簡潔,而庫本身依然臃腫。不知道為什么,現(xiàn)在我比較喜歡純C這種簡潔的東西,對于OO以及
template,漸漸地有點心累。
在功能實現(xiàn)上,我以TCP服務(wù)器為例,CreateEtwork根據(jù)傳來的參數(shù)建立服務(wù)器,在SocketManager::open中
是很常規(guī)的socket, bind, listen。當(dāng)建立了服務(wù)器之后,需要在程序主循環(huán)里不斷地輪詢狀態(tài),這里主要
調(diào)用poll函數(shù)完成。
poll函數(shù)主體就是調(diào)用select。當(dāng)select成功返回活動的套接字?jǐn)?shù)量后,Etwork依次輪詢讀、寫、錯誤fdset,
將保存的所有網(wǎng)絡(luò)連接(就是那些ISocket對象)對應(yīng)的套接字與fdset中當(dāng)前的套接字做比較。大致邏輯為:



















沒什么特別讓人注意的地方(別覺得別人垃圾,耐心讀別人的代碼不是什么壞事)。每一次,當(dāng)Etwork檢測到
新的連接時,會創(chuàng)建新的ISocket對象,并關(guān)聯(lián)對應(yīng)的套接字,然后保存此對象到一個列表中。當(dāng)poll結(jié)束
后,客戶端程序通常會調(diào)用accept函數(shù)(Etwork中提供的接口),該函數(shù)主要是將poll中保存的新的ISocket
對象全部拷貝出去。
在接收、發(fā)送網(wǎng)絡(luò)數(shù)據(jù)上,Etwork如同幾乎所有的網(wǎng)絡(luò)庫(模塊)一樣,采用了緩沖機制。這里所說的緩沖機
制是,網(wǎng)絡(luò)模塊接收到網(wǎng)絡(luò)數(shù)據(jù)時,將數(shù)據(jù)保存起來,客戶端程序想獲取數(shù)據(jù)時,實際上就是從這個緩沖中
直接取,而不是從網(wǎng)絡(luò)上獲取;同理,發(fā)送數(shù)據(jù)時,客戶端程序?qū)?shù)據(jù)提供給網(wǎng)絡(luò)模塊,網(wǎng)絡(luò)模塊將數(shù)據(jù)保
存起來,網(wǎng)絡(luò)模塊會在另一個時候發(fā)送這個緩沖中的數(shù)據(jù)(對于異步IO的處理畢竟不一樣)。
Etwork關(guān)于這個緩沖機制的相關(guān)代碼,主要集中在Buffer這個類。與Buffer相關(guān)的是一個Message機制。Buffer
維護了一個Message的隊列(deque)。一個Message實際上是一個非常簡單的結(jié)構(gòu)體:







這其實是消息頭,在消息頭后全部是數(shù)據(jù)。在創(chuàng)建消息時(new_message),Etwork根據(jù)客戶端提供的數(shù)據(jù)創(chuàng)建
足夠大的緩存保存:

這其實是一個很危險的做法,但是從Etwokr的源碼可以看出來,作者很喜歡玩弄這個技巧。與Buffer具體相
關(guān)的接口包括:get_data, put_data, get_message, put_message。Buffer內(nèi)部維護的數(shù)據(jù)都是以Message
的形式組織。但是,對于外部而言,卻依然是raw data,也就是諸如char*之類的數(shù)據(jù)。幾個相關(guān)函數(shù)大致
上的操作為:獲取指定尺寸的消息(可能包含多個消息),將一段數(shù)據(jù)加入Buffer并以消息的形式組織(可能會
創(chuàng)建多個消息),將一個消息以raw data的形式輸出,將raw data以一個消息的形式加入到Buffer。
一般情況下,Etwork的poll操作,會將套接字上的數(shù)據(jù)接收并put_data到緩沖中;發(fā)送數(shù)據(jù)時則get_data。
客戶端要從緩沖中獲取數(shù)據(jù)時,就調(diào)用get_message;發(fā)送數(shù)據(jù)時就put_message。
Etwork中還有一個比較有趣的東西:marshaller。這個東西主要就是提供將C++中各種數(shù)據(jù)類型的變量進行字
節(jié)編碼,也就是將int long struct之類的東西轉(zhuǎn)換為unsigned char,從而方便直接往網(wǎng)絡(luò)上發(fā)送。
基本上,Buffer和marshaller可以說是一個網(wǎng)絡(luò)庫(模塊)的必要部件,你可以在不同的網(wǎng)絡(luò)庫中看到類似的
東西。
Etwork在網(wǎng)絡(luò)事件的處理上,除了上面的輪詢外,還支持回調(diào)機制。這主要是通過INotify,以及給各個ISocket
注冊Notify對象實現(xiàn)。沒什么難度,基本上就是observer模式的簡單實現(xiàn)。
其他東西就沒什么好說的了,縱觀一下,Etwork實現(xiàn)得還是比較典型的,可以作為開發(fā)網(wǎng)絡(luò)庫的一個簡單例子。
posted on 2008-05-21 21:06 Kevin Lynx 閱讀(6995) 評論(5) 編輯 收藏 引用 所屬分類: network