libevent(1)
很多時候,除了響應(yīng)事件之外,應(yīng)用還希望做一定的數(shù)據(jù)緩沖。比如說,寫入數(shù)據(jù)的時候,通常的運行模式是:
l?決定要向連接寫入一些數(shù)據(jù),把數(shù)據(jù)放入到緩沖區(qū)中
l?等待連接可以寫入
l?寫入盡量多的數(shù)據(jù)
l?記住寫入了多少數(shù)據(jù),如果還有更多數(shù)據(jù)要寫入,等待連接再次可以寫入
這種緩沖IO模式很通用,libevent為此提供了一種通用機制,即bufferevent。bufferevent由一個底層的傳輸端口(如套接字),一個讀取緩沖區(qū)和一個寫入緩沖區(qū)組成。與通常的事件在底層傳輸端口已經(jīng)就緒,可以讀取或者寫入的時候執(zhí)行回調(diào)不同的是,bufferevent在讀取或者寫入了足夠量的數(shù)據(jù)之后調(diào)用用戶提供的回調(diào)。
有多種共享公用接口的bufferevent類型,編寫本文時已存在以下類型:
l?基于套接字的bufferevent:使用event_*接口作為后端,通過底層流式套接字發(fā)送或者接收數(shù)據(jù)的bufferevent
l?異步IO?bufferevent:使用Windows?IOCP接口,通過底層流式套接字發(fā)送或者接收數(shù)據(jù)的bufferevent(僅用于Windows,試驗中)
l?過濾型bufferevent:將數(shù)據(jù)傳輸?shù)降讓觔ufferevent對象之前,處理輸入或者輸出數(shù)據(jù)的bufferevent:比如說,為了壓縮或者轉(zhuǎn)換數(shù)據(jù)。
l?成對的bufferevent:相互傳輸數(shù)據(jù)的兩個bufferevent。
注意:截止2.0.2-alpha版,這里列出的bufferevent接口還沒有完全正交于所有的bufferevent類型。也就是說,下面將要介紹的接口不是都能用于所有bufferevent類型。libevent開發(fā)者在未來版本中將修正這個問題。
也請注意:當前bufferevent只能用于像TCP這樣的面向流的協(xié)議,將來才可能會支持像UDP這樣的面向數(shù)據(jù)報的協(xié)議。
本節(jié)描述的所有函數(shù)和類型都在event2/bufferevent.h中聲明。特別提及的關(guān)于evbuffer的函數(shù)聲明在event2/buffer.h中,詳細信息請參考下一章。
1?bufferevent和evbuffer
每個bufferevent都有一個輸入緩沖區(qū)和一個輸出緩沖區(qū),它們的類型都是“struct?evbuffer”。有數(shù)據(jù)要寫入到bufferevent時,添加數(shù)據(jù)到輸出緩沖區(qū);bufferevent中有數(shù)據(jù)供讀取的時候,從輸入緩沖區(qū)抽取(drain)數(shù)據(jù)。
evbuffer接口支持很多種操作,后面的章節(jié)將討論這些操作。
2?回調(diào)和水位
每個bufferevent有兩個數(shù)據(jù)相關(guān)的回調(diào):一個讀取回調(diào)和一個寫入回調(diào)。默認情況下,從底層傳輸端口讀取了任意量的數(shù)據(jù)之后會調(diào)用讀取回調(diào);輸出緩沖區(qū)中足夠量的數(shù)據(jù)被清空到底層傳輸端口后寫入回調(diào)會被調(diào)用。通過調(diào)整bufferevent的讀取和寫入“水位(watermarks)”可以覆蓋這些函數(shù)的默認行為。
每個bufferevent有四個水位:
l?讀取低水位:讀取操作使得輸入緩沖區(qū)的數(shù)據(jù)量在此級別或者更高時,讀取回調(diào)將被調(diào)用。默認值為0,所以每個讀取操作都會導(dǎo)致讀取回調(diào)被調(diào)用。
l?讀取高水位:輸入緩沖區(qū)中的數(shù)據(jù)量達到此級別后,bufferevent將停止讀取,直到輸入緩沖區(qū)中足夠量的數(shù)據(jù)被抽取,使得數(shù)據(jù)量低于此級別。默認值是無限,所以永遠不會因為輸入緩沖區(qū)的大小而停止讀取。
l?寫入低水位:寫入操作使得輸出緩沖區(qū)的數(shù)據(jù)量達到或者低于此級別時,寫入回調(diào)將被調(diào)用。默認值是0,所以只有輸出緩沖區(qū)空的時候才會調(diào)用寫入回調(diào)。
l?寫入高水位:bufferevent沒有直接使用這個水位。它在bufferevent用作另外一個bufferevent的底層傳輸端口時有特殊意義。請看后面關(guān)于過濾型bufferevent的介紹。
?
bufferevent也有“錯誤”或者“事件”回調(diào),用于向應(yīng)用通知非面向數(shù)據(jù)的事件,如連接已經(jīng)關(guān)閉或者發(fā)生錯誤。定義了下列事件標志:
l?BEV_EVENT_READING:讀取操作時發(fā)生某事件,具體是哪種事件請看其他標志。
l?BEV_EVENT_WRITING:寫入操作時發(fā)生某事件,具體是哪種事件請看其他標志。
l?BEV_EVENT_ERROR:操作時發(fā)生錯誤。關(guān)于錯誤的更多信息,請調(diào)用EVUTIL_SOCKET_ERROR()。
l?BEV_EVENT_TIMEOUT:發(fā)生超時。
l?BEV_EVENT_EOF:遇到文件結(jié)束指示。
l?BEV_EVENT_CONNECTED:請求的連接過程已經(jīng)完成。
上述標志由2.0.2-alpha版新引入。
3?延遲回調(diào)
默認情況下,bufferevent的回調(diào)在相應(yīng)的條件發(fā)生時立即被執(zhí)行。(evbuffer的回調(diào)也是這樣的,隨后會介紹)在依賴關(guān)系復(fù)雜的情況下,這種立即調(diào)用會制造麻煩。比如說,假如某個回調(diào)在evbuffer?A空的時候向其中移入數(shù)據(jù),而另一個回調(diào)在evbuffer?A滿的時候從中取出數(shù)據(jù)。這些調(diào)用都是在棧上發(fā)生的,在依賴關(guān)系足夠復(fù)雜的時候,有棧溢出的風險。
要解決此問題,可以請求bufferevent(或者evbuffer)延遲其回調(diào)。條件滿足時,延遲回調(diào)不會立即調(diào)用,而是在event_loop()調(diào)用中被排隊,然后在通常的事件回調(diào)之后執(zhí)行。
(延遲回調(diào)由libevent?2.0.1-alpha版引入)
4?bufferevent的選項標志
創(chuàng)建bufferevent時可以使用一個或者多個標志修改其行為。可識別的標志有:
l?BEV_OPT_CLOSE_ON_FREE:釋放bufferevent時關(guān)閉底層傳輸端口。這將關(guān)閉底層套接字,釋放底層bufferevent等。
l?BEV_OPT_THREADSAFE:自動為bufferevent分配鎖,這樣就可以安全地在多個線程中使用bufferevent。
l?BEV_OPT_DEFER_CALLBACKS:設(shè)置這個標志時,bufferevent延遲所有回調(diào),如上所述。
l?BEV_OPT_UNLOCK_CALLBACKS:默認情況下,如果設(shè)置bufferevent為線程安全的,則bufferevent會在調(diào)用用戶提供的回調(diào)時進行鎖定。設(shè)置這個選項會讓libevent在執(zhí)行回調(diào)的時候不進行鎖定。
(BEV_OPT_UNLOCK_CALLBACKS由2.0.5-beta版引入,其他選項由2.0.1-alpha版引入)
5?與基于套接字的bufferevent一起工作
基于套接字的bufferevent是最簡單的,它使用libevent的底層事件機制來檢測底層網(wǎng)絡(luò)套接字是否已經(jīng)就緒,可以進行讀寫操作,并且使用底層網(wǎng)絡(luò)調(diào)用(如readv、writev、WSASend、WSARecv)來發(fā)送和接收數(shù)據(jù)。
5.1?創(chuàng)建基于套接字的bufferevent
可以使用bufferevent_socket_new()創(chuàng)建基于套接字的bufferevent。
接口
base是event_base,options是表示bufferevent選項(BEV_OPT_CLOSE_ON_FREE等)的位掩碼,fd是一個可選的表示套接字的文件描述符。如果想以后設(shè)置文件描述符,可以設(shè)置fd為-1。
成功時函數(shù)返回一個bufferevent,失敗則返回NULL。
bufferevent_socket_new()函數(shù)由2.0.1-alpha版新引入。
5.2?在基于套接字的bufferevent上啟動連接
如果bufferevent的套接字還沒有連接上,可以啟動新的連接。
接口
address和addrlen參數(shù)跟標準調(diào)用connect()的參數(shù)相同。如果還沒有為bufferevent設(shè)置套接字,調(diào)用函數(shù)將為其分配一個新的流套接字,并且設(shè)置為非阻塞的。
如果已經(jīng)為bufferevent設(shè)置套接字,調(diào)用bufferevent_socket_connect()將告知libevent套接字還未連接,直到連接成功之前不應(yīng)該對其進行讀取或者寫入操作。
連接完成之前可以向輸出緩沖區(qū)添加數(shù)據(jù)。
如果連接成功啟動,函數(shù)返回0;如果發(fā)生錯誤則返回-1。
示例
bufferevent_socket_connect()函數(shù)由2.0.2-alpha版引入。在此之前,必須自己手動在套接字上調(diào)用connect(),連接完成時,bufferevent將報告寫入事件。
注意:如果使用bufferevent_socket_connect()發(fā)起連接,將只會收到BEV_EVENT_CONNECTED事件。如果自己調(diào)用connect(),則連接上將被報告為寫入事件。
這個函數(shù)在2.0.2-alpha版引入。
?
5.3?通過主機名啟動連接
常常需要將解析主機名和連接到主機合并成單個操作,libevent為此提供了:
接口
這個函數(shù)解析名字hostname,查找其family類型的地址(允許的地址族類型有AF_INET,AF_INET6和AF_UNSPEC)。如果名字解析失敗,函數(shù)將調(diào)用事件回調(diào),報告錯誤事件。如果解析成功,函數(shù)將啟動連接請求,就像bufferevent_socket_connect()一樣。
dns_base參數(shù)是可選的:如果為NULL,等待名字查找完成期間調(diào)用線程將被阻塞,而這通常不是期望的行為;如果提供dns_base參數(shù),libevent將使用它來異步地查詢主機名。關(guān)于DNS的更多信息,請看第九章。
跟bufferevent_socket_connect()一樣,函數(shù)告知libevent,bufferevent上現(xiàn)存的套接字還沒有連接,在名字解析和連接操作成功完成之前,不應(yīng)該對套接字進行讀取或者寫入操作。
函數(shù)返回的錯誤可能是DNS主機名查詢錯誤,可以調(diào)用bufferevent_socket_get_dns_error()來獲取最近的錯誤。返回值0表示沒有檢測到DNS錯誤。
示例:簡單的HTTP?v0客戶端
6?通用bufferevent操作
本節(jié)描述的函數(shù)可用于多種bufferevent實現(xiàn)。
6.1?釋放bufferevent
接口
這個函數(shù)釋放bufferevent。bufferevent內(nèi)部具有引用計數(shù),所以,如果釋放bufferevent時還有未決的延遲回調(diào),則在回調(diào)完成之前bufferevent不會被刪除。
如果設(shè)置了BEV_OPT_CLOSE_ON_FREE標志,并且bufferevent有一個套接字或者底層bufferevent作為其傳輸端口,則釋放bufferevent將關(guān)閉這個傳輸端口。
這個函數(shù)由libevent?0.8版引入。
6.2?操作回調(diào)、水位和啟用/禁用
接口
bufferevent_setcb() 函數(shù)修改bufferevent的一個或者多個回調(diào)。readcb、writecb和eventcb函數(shù)將分別在已經(jīng)讀取足夠的數(shù)據(jù)、已經(jīng)寫入足夠的數(shù) 據(jù),或者發(fā)生錯誤時被調(diào)用。每個回調(diào)函數(shù)的第一個參數(shù)都是發(fā)生了事件的bufferevent,最后一個參數(shù)都是調(diào)用 bufferevent_setcb()時用戶提供的cbarg參數(shù):可以通過它向回調(diào)傳遞數(shù)據(jù)。事件回調(diào)的events參數(shù)是一個表示事件標志的位掩碼:請看前面的“回調(diào)和水位”節(jié)。
要禁用回調(diào),傳遞NULL而不是回調(diào)函數(shù)。注意:bufferevent的所有回調(diào)函數(shù)共享單個cbarg,所以修改它將影響所有回調(diào)函數(shù)。
這個函數(shù)由1.4.4版引入。類型名bufferevent_data_cb和bufferevent_event_cb由2.0.2-alpha版引入。
接口
可以啟用或者禁用bufferevent上的EV_READ、EV_WRITE或者EV_READ?|?EV_WRITE事件。沒有啟用讀取或者寫入事件時,bufferevent將不會試圖進行數(shù)據(jù)讀取或者寫入。
沒有必要在輸出緩沖區(qū)空時禁用寫入事件:bufferevent將自動停止寫入,然后在有數(shù)據(jù)等待寫入時重新開始。
類似地,沒有必要在輸入緩沖區(qū)高于高水位時禁用讀取事件:bufferevent將自動停止讀取,然后在有空間用于讀取時重新開始讀取。
默認情況下,新創(chuàng)建的bufferevent的寫入是啟用的,但是讀取沒有啟用。
可以調(diào)用bufferevent_get_enabled()確定bufferevent上當前啟用的事件。
除了bufferevent_get_enabled()由2.0.3-alpha版引入外,這些函數(shù)都由0.8版引入。
接口
bufferevent_setwatermark()函數(shù)調(diào)整單個bufferevent的讀取水位、寫入水位,或者同時調(diào)整二者。(如果events參數(shù)設(shè)置了EV_READ,調(diào)整讀取水位。如果events設(shè)置了EV_WRITE標志,調(diào)整寫入水位)
對于高水位,0表示“無限”。
這個函數(shù)首次出現(xiàn)在1.4.4版。
示例
6.3?操作bufferevent中的數(shù)據(jù)
如果只是通過網(wǎng)絡(luò)讀取或者寫入數(shù)據(jù),而不能觀察操作過程,是沒什么好處的。bufferevent提供了下列函數(shù)用于觀察要寫入或者讀取的數(shù)據(jù)。(Reading?and?writing?data?from?the?network?does?you?no?good?if?you?can't?look?at?it.Bufferevents?give?you?these?methods?to?give?them?data?to?write,and?to?get?the?data?to?read.)
接口
這兩個函數(shù)提供了非常強大的基礎(chǔ):它們分別返回輸入和輸出緩沖區(qū)。關(guān)于可以對evbuffer類型進行的所有操作的完整信息,請看下一章。
如果寫入操作因為數(shù)據(jù)量太少而停止(或者讀取操作因為太多數(shù)據(jù)而停止),則向輸出緩沖區(qū)添加數(shù)據(jù)(或者從輸入緩沖區(qū)移除數(shù)據(jù))將自動重啟操作。
這些函數(shù)由2.0.1-alpha版引入。
接口
這些函數(shù)向bufferevent的輸出緩沖區(qū)添加數(shù)據(jù)。bufferevent_write()將內(nèi)存中從data處開始的size字節(jié)數(shù)據(jù)添加到輸出緩沖區(qū)的末尾。bufferevent_write_buffer()移除buf的所有內(nèi)容,將其放置到輸出緩沖區(qū)的末尾。成功時這些函數(shù)都返回0,發(fā)生錯誤時則返回-1。
這些函數(shù)從0.8版就存在了。
接口
這 些函數(shù)從bufferevent的輸入緩沖區(qū)移除數(shù)據(jù)。bufferevent_read()至多從輸入緩沖區(qū)移除size字節(jié)的數(shù)據(jù),將其存儲到內(nèi)存中 data處。函數(shù)返回實際移除的字節(jié)數(shù)。bufferevent_read_buffer()函數(shù)抽空輸入緩沖區(qū)的所有內(nèi)容,將其放置到buf中,成功時 返回0,失敗時返回-1。
注意,對于bufferevent_read(),data處的內(nèi)存塊必須有足夠的空間容納size字節(jié)數(shù)據(jù)。
bufferevent_read()函數(shù)從0.8版就存在了;bufferevnet_read_buffer()由2.0.1-alpha版引入。
示例
6.4?讀寫超時
跟其他事件一樣,可以要求在一定量的時間已經(jīng)流逝,而沒有成功寫入或者讀取數(shù)據(jù)的時候調(diào)用一個超時回調(diào)。
接口
設(shè)置超時為NULL會移除超時回調(diào)。
試圖讀取數(shù)據(jù)的時候,如果至少等待了timeout_read秒,則讀取超時事件將被觸發(fā)。試圖寫入數(shù)據(jù)的時候,如果至少等待了timeout_write秒,則寫入超時事件將被觸發(fā)。
注意,只有在讀取或者寫入的時候才會計算超時。也就是說,如果bufferevent的讀取被禁止,或者輸入緩沖區(qū)滿(達到其高水位),則讀取超時被禁止。類似的,如果寫入被禁止,或者沒有數(shù)據(jù)待寫入,則寫入超時被禁止。
讀取或者寫入超時發(fā)生時,相應(yīng)的讀取或者寫入操作被禁止,然后超時事件回調(diào)被調(diào)用,帶有標志BEV_EVENT_TIMEOUT?|?BEV_EVENT_READING或者BEV_EVENT_TIMEOUT?|?BEV_EVENT_WRITING。
這個函數(shù)從2.0.1-alpha版就存在了,但是直到2.0.4-alpha版才對于各種bufferevent類型行為一致。
6.5?對bufferevent發(fā)起清空操作
接口
清空bufferevent要求bufferevent強制從底層傳輸端口讀取或者寫入盡可能多的數(shù)據(jù),而忽略其他可能保持數(shù)據(jù)不被寫入的限制條件。函數(shù)的細節(jié)功能依賴于bufferevent的具體類型。
iotype參數(shù)應(yīng)該是EV_READ、EV_WRITE或者EV_READ?|?EV_WRITE,用于指示應(yīng)該處理讀取、寫入,還是二者都處理。state參數(shù)可以是BEV_NORMAL、BEV_FLUSH或者BEV_FINISHED。BEV_FINISHED指示應(yīng)該告知另一端,沒有更多數(shù)據(jù)需要發(fā)送了;而BEV_NORMAL和BEV_FLUSH的區(qū)別依賴于具體的bufferevent類型。
失敗時bufferevent_flush()返回-1,如果沒有數(shù)據(jù)被清空則返回0,有數(shù)據(jù)被清空則返回1。
當前(2.0.5-beta版)僅有一些bufferevent類型實現(xiàn)了bufferevent_flush()。特別是,基于套接字的bufferevent沒有實現(xiàn)。
7?類型特定的bufferevent函數(shù)
這些bufferevent函數(shù)不能支持所有bufferevent類型。
接口
這個函數(shù)調(diào)整bufev的優(yōu)先級為pri。關(guān)于優(yōu)先級的更多信息請看event_priority_set()。
成功時函數(shù)返回0,失敗時返回-1。這個函數(shù)僅能用于基于套接字的bufferevent。
這個函數(shù)由1.0版引入。
接口
這些函數(shù)設(shè)置或者返回基于fd的事件的文件描述符。只有基于套接字的bufferevent支持setfd()。兩個函數(shù)都在失敗時返回-1;setfd()成功時返回0。
bufferevent_setfd()函數(shù)由1.4.4版引入;bufferevent_getfd()函數(shù)由2.0.2-alpha版引入。
接口
這個函數(shù)返回bufferevent的event_base,由2.0.9-rc版引入。
接口
這個函數(shù)返回作為bufferevent底層傳輸端口的另一個bufferevent。關(guān)于這種情況,請看關(guān)于過濾型bufferevent的介紹。
這個函數(shù)由2.0.2-alpha版引入。
8?手動鎖定和解鎖
有時候需要確保對bufferevent的一些操作是原子地執(zhí)行的。為此,libevent提供了手動鎖定和解鎖bufferevent的函數(shù)。
接口
注意:如果創(chuàng)建bufferevent時沒有指定BEV_OPT_THREADSAFE標志,或者沒有激活libevent的線程支持,則鎖定操作是沒有效果的。
用這個函數(shù)鎖定bufferevent將自動同時鎖定相關(guān)聯(lián)的evbuffer。這些函數(shù)是遞歸的:鎖定已經(jīng)持有鎖的bufferevent是安全的。當然,對于每次鎖定都必須進行一次解鎖。
這些函數(shù)由2.0.6-rc版引入。
9?已廢棄的bufferevent功能
從1.4到2.0版,bufferevent的后端代碼一直在進行修訂。在老的接口中,訪問bufferevent結(jié)構(gòu)體的內(nèi)部是很平常的,并且還會使用依賴于這種訪問的宏。
更復(fù)雜的是,老的代碼有時候?qū)ⅰ癳vbuffer”前綴用于bufferevent功能。
這里有一個在2.0版之前使用過的東西的概要:
老的函數(shù)定義在event.h中,而不是在event2/bufferevent.h中。
如果仍然需要訪問bufferevent結(jié)構(gòu)體內(nèi)部的某些公有部分,可以包含event2/bufferevent_struct.h。但是不建議這么做:不同版本的Libevent中bufferevent結(jié)構(gòu)體的內(nèi)容可能會改變。本節(jié)描述的宏和名字只有在包含了event2/bufferevent_compat.h時才能使用。
較老版本中用于設(shè)置bufferevent的接口有所不同:
接口
bufferevent_new()函數(shù)僅僅在已經(jīng)廢棄的“默認”event_base上創(chuàng)建一個套接字bufferevent。調(diào)用bufferevent_base_set()可以調(diào)整套接字bufferevent的event_base。
較老版本不使用timeval結(jié)構(gòu)體設(shè)置超時,而是使用秒數(shù):
接口
最后要指出的是,2.0之前版本中的evbuffer實現(xiàn)是極其低效的,這對將bufferevent用于高性能應(yīng)用是一個問題。
轉(zhuǎn)載于:https://www.cnblogs.com/zzyoucan/p/3751166.html
總結(jié)
以上是生活随笔為你收集整理的libevent(1)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 转载:Spring AOP (下)
- 下一篇: 举例什么时候会用到 call(), ap