在libevent中還有一個很重要的部分。evbuffer和bufferevent。
evbuffer是一個緩衝區,用戶可以向evbuffer添加數據,evbuffer和bufferevent經常一起使用,或者說bufferevent使用了evbuffer,bufferevent有兩個evbuffer緩衝,還有event。
說明bufferevent是一個帶有緩衝區的I/O。可以使用它們!用戶可以通過bufferevent相關的函數往evbuffer緩衝區中添加需要發送的內同,內部機制可以保證發送。當有數據在bufferevent上的套接字上時,數據被讀入到bufferevent內部的evbuffer緩衝區!
這樣一來,用戶程序需要交互的就是使用bufferevent相關函數想bufferevent內部的兩個evbuffer(一個爲讀緩衝,一個爲寫緩衝)寫數據/取數據,發送和接受都是bufferevent內部實現的。當然內部的發送和讀取都是非阻塞的!!
其中evbuffer並沒有顯示的使用malloc在堆區申請空間,而是使用了內核爲每個套接字在棧上分配的事件,在這個空間不夠時,再申請空間存放數據,使用函數evbuffer_expand()函數。具體的步驟是怎麼回事呢?(這裏分析的是libevent-1.4.12-stable)
一、首先申請bufferevent
使用bufferevent_new函數申請一個struct bufferevent.首先是malloc了一些結構體,然後調用了兩個重量級的函數,event_set,這個函數被調用兩次,分別對應bufferevent中的ev_read和ev_write。這個函數是幹什麼的,在不久以前的某個時刻也注重分析了,就是讓某一個套接字初始化給具體的event,並且初始化這個event所要關注的事件和事件處理函數,這麼一來,bufferevent中的兩個event被初始化了!暫且不說註冊的兩個函數(bufferevent_readcb
bufferevent_writecb),這也是重量級的。
然後調用函數bufferevent_setcb函數,這個函數註冊層的回調函數,這函數處理了從evbuffer中讀取數據後的相應處理(怎麼處理就是用戶層的事情了)
二、調用bufferevent_enable函數
這個函數中同樣調用了一個以前的重量級函數event_add(),這個函數可以理解爲將某個event添加到event_base中,這樣這個套接字就可以被處理,等到有數據到達就會被觸發。其實到目前爲止,已經可以結束了,上下的就是自己寫bufferevent_setcb函數中註冊的兩個函數了。
整天看着兩步,再對比以前使用libevent的流程,之前多一個event_init全局初始化,然後多一個event_dispatch()函數,也就是說,這個bufferevent知識多了兩個evbuffer,然後對event_set函數和event_add函數進行再次封裝!
三、bufferevent_readcb()
這個函數是在buffer_new函數中別event_set函數調用的,event_set也就是對傳遞給的event參數做相應的初始化,但是bufferevent_readcb函數的具體內容是什麼呢?
咱們這裏不考慮什麼讀/寫的標準,都是默認值。在這個函數中調用了evbuffer_read函數,這個函數可以直接調用了系統調用read(),,讀取的函數就是直接調用bufferevent_add函數繼續關注這個event
四、bufferevent_write()函數
其實和上面的那個函數類似,調用了evbuffer_write函數,這個函數總調用了write函數,然後調用bufferevent_add函數繼續關注這個事件
這是使用bufferevent的整個過程!!
其實想要知道libevent是怎麼使用evbuffer,還需要知道是怎麼調用的!
2 回調和水位
每個bufferevent有兩個數據相關的回調:一個讀取回調和一個寫入回調。默認情況下,從底層傳輸端口讀取了任意量的數據之後會調用讀取回調;輸出緩衝區中足夠量的數據被清空到底層傳輸端口後寫入回調會被調用。通過調整bufferevent的讀取和寫入“水位(watermarks)”可以覆蓋這些函數的默認行爲。
每個bufferevent有四個水位:
l 讀取低水位:讀取操作使得輸入緩衝區的數據量在此級別或者更高時,讀取回調將被調用。默認值爲0,所以每個讀取操作都會導致讀取回調被調用。
l 讀取高水位:輸入緩衝區中的數據量達到此級別後,bufferevent將停止讀取,直到輸入緩衝區中足夠量的數據被抽取,使得數據量低於此級別。默認值是無限,所以永遠不會因爲輸入緩衝區的大小而停止讀取。
l 寫入低水位:寫入操作使得輸出緩衝區的數據量達到或者低於此級別時,寫入回調將被調用。默認值是0,所以只有輸出緩衝區空的時候纔會調用寫入回調。
l 寫入高水位:bufferevent沒有直接使用這個水位。它在bufferevent用作另外一個bufferevent的底層傳輸端口時有特殊意義。請看後面關於過濾型bufferevent的介紹。
bufferevent也有“錯誤”或者“事件”回調,用於向應用通知非面向數據的事件,如連接已經關閉或者發生錯誤。定義了下列事件標誌:
l BEV_EVENT_READING:讀取操作時發生某事件,具體是哪種事件請看其他標誌。
l BEV_EVENT_WRITING:寫入操作時發生某事件,具體是哪種事件請看其他標誌。
l BEV_EVENT_ERROR:操作時發生錯誤。關於錯誤的更多信息,請調用EVUTIL_SOCKET_ERROR()。
l BEV_EVENT_TIMEOUT:發生超時。
l BEV_EVENT_EOF:遇到文件結束指示。
l BEV_EVENT_CONNECTED:請求的連接過程已經完成。
上述標誌由2.0.2-alpha版新引入。
3 延遲迴調
默認情況下,bufferevent的回調在相應的條件發生時立即被執行。(evbuffer的回調也是這樣的,隨後會介紹)在依賴關係複雜的情況下,這種立即調用會製造麻煩。比如說,假如某個回調在evbuffer A空的時候向其中移入數據,而另一個回調在evbuffer A滿的時候從中取出數據。這些調用都是在棧上發生的,在依賴關係足夠複雜的時候,有棧溢出的風險。
要解決此問題,可以請求bufferevent(或者evbuffer)延遲其回調。條件滿足時,延遲迴調不會立即調用,而是在event_loop()調用中被排隊,然後在通常的事件回調之後執行。
(延遲迴調由libevent 2.0.1-alpha版引入)