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

for( each fd in readset )
if( fd is listening fd )
accept new connection
else
for( each socket in all connections )
if( fd == socket )
can read data on this socket

for( each fd in writeset )


for( each fd in exceptset )



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


{
unsigned short offset_;
unsigned short size_;
};
這其實是消息頭,在消息頭后全部是數據。在創建消息時(new_message),Etwork根據客戶端提供的數據創建
足夠大的緩存保存:
Message * m = (Message *)::operator new( size + sizeof( Message ) );
這其實是一個很危險的做法,但是從Etwokr的源碼可以看出來,作者很喜歡玩弄這個技巧。與Buffer具體相
關的接口包括:get_data, put_data, get_message, put_message。Buffer內部維護的數據都是以Message
的形式組織。但是,對于外部而言,卻依然是raw data,也就是諸如char*之類的數據。幾個相關函數大致
上的操作為:獲取指定尺寸的消息(可能包含多個消息),將一段數據加入Buffer并以消息的形式組織(可能會
創建多個消息),將一個消息以raw data的形式輸出,將raw data以一個消息的形式加入到Buffer。
一般情況下,Etwork的poll操作,會將套接字上的數據接收并put_data到緩沖中;發送數據時則get_data。
客戶端要從緩沖中獲取數據時,就調用get_message;發送數據時就put_message。
Etwork中還有一個比較有趣的東西:marshaller。這個東西主要就是提供將C++中各種數據類型的變量進行字
節編碼,也就是將int long struct之類的東西轉換為unsigned char,從而方便直接往網絡上發送。
基本上,Buffer和marshaller可以說是一個網絡庫(模塊)的必要部件,你可以在不同的網絡庫中看到類似的
東西。
Etwork在網絡事件的處理上,除了上面的輪詢外,還支持回調機制。這主要是通過INotify,以及給各個ISocket
注冊Notify對象實現。沒什么難度,基本上就是observer模式的簡單實現。
其他東西就沒什么好說的了,縱觀一下,Etwork實現得還是比較典型的,可以作為開發網絡庫的一個簡單例子。