• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 200, comments - 8, trackbacks - 0, articles - 0

            譯自http://www.wangafu.net/~nickm/libevent-book/Ref6a_advanced_bufferevents.html

             

            本章描述bufferevent的一些對通常使用不必要的高級特征。如果只想學習如何使用bufferevent,可以跳過這一章,直接閱讀下一章。

            成對的bufferevent

            有時候網絡程序需要與自身通信。比如說,通過某些協議對用戶連接進行隧道操作的程序,有時候也需要通過同樣的協議對自身的連接進行隧道操作。當然,可以通過打開一個到自身監聽端口的連接,讓程序使用這個連接來達到這種目標。但是,通過網絡棧來與自身通信比較浪費資源。

            替代的解決方案是,創建一對成對的bufferevent。這樣,寫入到一個bufferevent的字節都被另一個接收(反過來也是),但是不需要使用套接字。

            接口
            int bufferevent_pair_new(struct event_base *baseint options,
                
            struct bufferevent *pair[2]);

            調用bufferevent_pair_new()會設置pair[0]和pair[1]為一對相互連接的bufferevent。除了BEV_OPT_CLOSE_ON_FREE無效、BEV_OPT_DEFER_CALLBACKS總是打開的之外,所有通常的選項都是支持的。

            為什么bufferevent對需要帶延遲回調運行?通常某一方上的操作會調用一個通知另一方的回調,從而調用另一方的回調,如此這樣進行很多步。如果不延遲回調,這種調用鏈常常會導致棧溢出或者餓死其他連接,而且還要求所有的回調是可重入的。

            成對的bufferevent支持flush:設置模式參數為BEV_NORMAL或者BEV_FLUSH會強制要求所有相關數據從對中的一個bufferevent傳輸到另一個中,而忽略可能會限制傳輸的水位設置。增加BEV_FINISHED到模式參數中還會讓對端的bufferevent產生EOF事件。

            釋放對中的任何一個成員不會自動釋放另一個,也不會產生EOF事件。釋放僅僅會使對中的另一個成員成為斷開的。bufferevent一旦斷開,就不能再成功讀寫數據或者產生任何事件了。

            接口

            struct bufferevent *bufferevent_pair_get_partner(struct bufferevent *bev)

            有時候在給出了對的一個成員時,需要獲取另一個成員,這時候可以使用bufferevent_pair_get_partner()。如果bev是對的成員,而且對的另一個成員仍然存在,函數將返回另一個成員;否則,函數返回NULL。

            bufferevent對由2.0.1-alpha版本引入,而bufferevent_pair_get_partner()函數由2.0.6版本引入。

            過濾bufferevent

            有時候需要轉換傳遞給某bufferevent的所有數據,這可以通過添加一個壓縮層,或者將協議包裝到另一個協議中進行傳輸來實現。

            接口

            enum bufferevent_filter_result {
                    BEV_OK 
            = 0,
                    BEV_NEED_MORE 
            = 1,
                    BEV_ERROR 
            = 2
            };
            typedef 
            enum bufferevent_filter_result (*bufferevent_filter_cb)(
                
            struct evbuffer *source, struct evbuffer *destination, ev_ssize_t dst_limit,
                
            enum bufferevent_flush_mode mode, void *ctx);


            struct bufferevent *bufferevent_filter_new(struct bufferevent *underlying,
                    bufferevent_filter_cb input_filter,
                    bufferevent_filter_cb output_filter,
                    
            int options,
                    
            void (*free_context)(void *),
                    
            void *ctx);

            bufferevent_filter_new()函數創建一個封裝現有的“底層”bufferevent的過濾bufferevent。所有通過底層bufferevent接收的數據在到達過濾bufferevent之前都會經過“輸入”過濾器的轉換;所有通過底層bufferevent發送的數據在被發送到底層bufferevent之前都會經過“輸出”過濾器的轉換。

            向底層bufferevent添加過濾器將替換其回調函數。可以向底層bufferevent的evbuffer添加回調函數,但是如果想讓過濾器正確工作,就不能再設置bufferevent本身的回調函數。

            input_filter和output_filter函數將隨后描述。options參數支持所有通常的選項。如果設置了BEV_OPT_CLOSE_ON_FREE,那么釋放過濾bufferevent也會同時釋放底層bufferevent。ctx參數是傳遞給過濾函數的任意指針;如果提供了free_context,則在釋放ctx之前它會被調用。

            底層輸入緩沖區有數據可讀時,輸入過濾器函數會被調用;過濾器的輸出緩沖區有新的數據待寫入時,輸出過濾器函數會被調用。兩個過濾器函數都有一對evbuffer參數:從source讀取數據;向destination寫入數據,而dst_limit參數描述了可以寫入destination的字節數上限。過濾器函數可以忽略這個參數,但是這樣可能會違背高水位或者速率限制。如果dst_limit是-1,則沒有限制。mode參數向過濾器描述了寫入的方式。值BEV_NORMAL表示應該在方便轉換的基礎上寫入盡可能多的數據;而BEV_FLUSH表示寫入盡可能多的數據;BEV_FINISHED表示過濾器函數應該在流的末尾執行額外的清理操作。最后,過濾器函數的ctx參數就是傳遞給bufferevent_filter_new()函數的指針(ctx參數)。

            如果成功向目標緩沖區寫入了任何數據,過濾器函數應該返回BEV_OK;如果不獲得更多的輸入,或者不使用不同的清空(flush)模式,就不能向目標緩沖區寫入更多的數據,則應該返回BEV_NEED_MORE;如果過濾器上發生了不可恢復的錯誤,則應該返回BEV_ERROR。

            創建過濾器將啟用底層bufferevent的讀取和寫入。隨后就不需要自己管理讀取和寫入了:過濾器在不想讀取的時候會自動掛起底層bufferevent的讀取。從2.0.8-rc版本開始,可以在過濾器之外獨立地啟用/禁用底層bufferevent的讀取和寫入。然而,這樣可能會讓過濾器不能成功取得所需要的數據。

            不需要同時指定輸入和輸出過濾器:沒有給定的過濾器將被一個不進行數據轉換的過濾器取代。

            bufferevent和速率限制

            某些程序需要限制單個或者一組bufferevent使用的帶寬。2.0.4-alpha和2.0.5-alpha版本添加了為單個或者一組bufferevent設置速率限制的基本功能。

            3.1 速率限制模型

            libevent的速率限制使用記號存儲器(token bucket)算法確定在某時刻可以寫入或者讀取多少字節。每個速率限制對象在任何給定時刻都有一個“讀存儲器(read bucket)”和一個“寫存儲器(write bucket)”,其大小決定了對象可以立即讀取或者寫入多少字節。每個存儲器有一個填充速率,一個最大突發尺寸,和一個時間單位,或者說“滴答(tick)”。一個時間單位流逝后,存儲器被填充一些字節(決定于填充速率)——但是如果超過其突發尺寸,則超出的字節會丟失。

            因此,填充速率決定了對象發送或者接收字節的最大平均速率,而突發尺寸決定了在單次突發中可以發送或者接收的最大字節數;時間單位則確定了傳輸的平滑程度。

            3.2 為bufferevent設置速率限制

            接口

            #define EV_RATE_LIMIT_MAX EV_SSIZE_MAX
            struct ev_token_bucket_cfg;
            struct ev_token_bucket_cfg *ev_token_bucket_cfg_new(
                    size_t read_rate, sizee_t read_burst,
                    size_t write_rate, size_t write_burst,
                    
            const struct timeval *tick_len);
            void ev_token_bucket_cfg_free(struct ev_token_bucket_cfg *cfg);
            int bufferevent_set_rate_limit(struct bufferevent *bev,
                
            struct ev_token_bucket_cfg *cfg);

            ev_token_bucket_cfg結構體代表用于限制單個或者一組bufferevent的一對記號存儲器的配置值。要創建ev_token_bucket_cfg,調用ev_token_bucket_cfg_new函數,提供最大平均讀取速率、最大突發讀取量、最大平均寫入速率、最大突發寫入量,以及一個滴答的長度。如果tick_len參數為NULL,則默認的滴答長度為一秒。如果發生錯誤,函數會返回NULL。

            注意:read_rate和write_rate參數的單位是字節每滴答。也就是說,如果滴答長度是十分之一秒,read_rate是300,則最大平均讀取速率是3000字節每秒。此外,不支持大于EV_RATE_LIMIT_MAX的速率或者突發量。

            要限制bufferevent的傳輸速率,使用一個ev_token_bucket_cfg,對其調用bufferevent_set_rate_limit()。成功時函數返回0,失敗時返回-1。可以對任意數量的bufferevent使用相同的ev_token_bucket_cfg。要移除速率限制,可以調用bufferevent_set_rate_limit(),傳遞NULL作為cfg參數值。

            調用ev_token_bucket_cfg_free()可以釋放ev_token_bucket_cfg。注意:當前在沒有任何bufferevent使用ev_token_bucket_cfg之前進行釋放是不安全的。

            3.3 為一組bufferevent設置速率限制

            如果要限制一組bufferevent總的帶寬使用,可以將它們分配到一個速率限制組中。

            接口

            struct bufferevent_rate_limit_group;

            struct bufferevent_rate_limit_group *bufferevent_rate_limit_group_new(
                    
            struct event_base *base,
                    
            const struct ev_token_bucket_cfg *cfg);
            int bufferevent_rate_limit_group_set_cfg(
                    
            struct bufferevent_rate_limit_group *group,
                    
            const struct ev_token_bucket_cfg *cfg);
            void bufferevent_rate_limit_group_free(struct bufferevent_rate_limit_group *);
            int bufferevent_add_to_rate_limit_group(struct bufferevent *bev,
                
            struct bufferevent_rate_limit_group *g);
            int bufferevent_remove_from_rate_limit_group(struct bufferevent *bev);

            要創建速率限制組,使用一個event_base和一個已經初始化的ev_token_bucket_cfg作為參數調用bufferevent_rate_limit_group_new函數。使用bufferevent_add_to_rate_limit_group將bufferevent添加到組中;使用bufferevent_remove_from_rate_limit_group從組中刪除bufferevent。這些函數成功時返回0,失敗時返回-1。

            單個bufferevent在某時刻只能是一個速率限制組的成員。bufferevent可以同時有單獨的速率限制(通過bufferevent_set_rate_limit設置)和組速率限制。設置了這兩個限制時,對每個bufferevent,較低的限制將被應用。

            調用bufferevent_rate_limit_group_set_cfg修改組的速率限制。函數成功時返回0,失敗時返回-1。bufferevent_rate_limit_group_free函數釋放速率限制組,移除所有成員。

            在2.0版本中,組速率限制試圖實現總體的公平,但是具體實現可能在小的時間范圍內并不公平。如果你強烈關注調度的公平性,請幫助提供未來版本的補丁。

            3.4 檢查當前速率限制

            有時候需要得知應用到給定bufferevent或者組的速率限制,為此,libevent提供了函數:

            接口

            ev_ssize_t bufferevent_get_read_limit(struct bufferevent *bev);
            ev_ssize_t bufferevent_get_write_limit(
            struct bufferevent *bev);
            ev_ssize_t bufferevent_rate_limit_group_get_read_limit(
                    
            struct bufferevent_rate_limit_group *);
            ev_ssize_t bufferevent_rate_limit_group_get_write_limit(
                    
            struct bufferevent_rate_limit_group *);

            上述函數返回以字節為單位的bufferevent或者組的讀寫記號存儲器大小。注意:如果bufferevent已經被強制超過其配置(清空(flush)操作就會這樣),則這些值可能是負數。

            接口

            ev_ssize_t bufferevent_get_max_to_read(struct bufferevent *bev);
            ev_ssize_t bufferevent_get_max_to_write(
            struct bufferevent *bev);

            這些函數返回在考慮了應用到bufferevent或者組(如果有)的速率限制,以及一次最大讀寫數據量的情況下,現在可以讀或者寫的字節數。

            接口

            void bufferevent_rate_limit_group_get_totals(
                
            struct bufferevent_rate_limit_group *grp,
                ev_uint64_t 
            *total_read_out, ev_uint64_t *total_written_out);
            void bufferevent_rate_limit_group_reset_totals(
                
            struct bufferevent_rate_limit_group *grp);

            每個bufferevent_rate_limit_group跟蹤經過其發送的總的字節數,這可用于跟蹤組中所有bufferevent總的使用情況。對一個組調用bufferevent_rate_limit_group_get_totals會分別設置total_read_out和total_written_out為組的總讀取和寫入字節數。組創建的時候這些計數從0開始,調用bufferevent_rate_limit_group_reset_totals會復位計數為0。

            3.5 手動調整速率限制

            對于有復雜需求的程序,可能需要調整記號存儲器的當前值。比如說,如果程序通過使用bufferevent的方式產生一些通信量時

            接口

            int bufferevent_decrement_read_limit(struct bufferevent *bev, ev_ssize_t decr);
            int bufferevent_decrement_write_limit(struct bufferevent *bev, ev_ssize_t decr);
            int bufferevent_rate_limit_group_decrement_read(
                    
            struct bufferevent_rate_limit_group *grp, ev_ssize_t decr);
            int bufferevent_rate_limit_group_decrement_write(
                    
            struct bufferevent_rate_limit_group *grp, ev_ssize_t decr);

            這些函數減小某個bufferevent或者速率限制組的當前讀或者寫存儲器。注意:減小是有符號的。如果要增加存儲器,就傳入負值。

            3.6 設置速率限制組的最小可能共享

            通常,不希望在每個滴答中為速率限制組中的所有bufferevent平等地分配可用的字節。比如說,有一個含有10000個活動bufferevent的速率限制組,它在每個滴答中可以寫入10000字節,那么,因為系統調用和TCP頭部的開銷,讓每個bufferevent在每個滴答中僅寫入1字節是低效的。

            為解決此問題,速率限制組有一個“最小共享(minimum share)”的概念。在上述情況下,不是允許每個bufferevent在每個滴答中寫入1字節,而是在每個滴答中允許某個bufferevent寫入一些(最小共享)字節,而其余的bufferevent將不允許寫入。允許哪個bufferevent寫入將在每個滴答中隨機選擇。

            默認的最小共享值具有較好的性能,當前(2.0.6-rc版本)其值為64。可以通過這個函數調整最小共享值:

            接口

            int bufferevent_rate_limit_group_set_min_share(
                    
            struct bufferevent_rate_limit_group *group, size_t min_share);

            設置min_share為0將會完全禁止最小共享。

            速率限制功能從引入開始就具有最小共享了,而修改最小共享的函數在2.0.6-rc版本首次引入。

            3.7 速率限制實現的限制

            2.0版本的libevent的速率限制具有一些實現上的限制:

            不是每種bufferevent類型都良好地或者說完整地支持速率限制。

            bufferevent速率限制組不能嵌套,一個bufferevent在某時刻只能屬于一個速率限制組。

            速率限制實現僅計算TCP分組傳輸的數據,不包括TCP頭部。

            讀速率限制實現依賴于TCP棧通知應用程序僅僅以某速率消費數據,并且在其緩沖區滿的時候將數據推送到TCP連接的另一端。

            某些bufferevent實現(特別是Windows中的IOCP實現)可能調撥過度。

            存儲器開始于一個滴答的通信量。這意味著bufferevent可以立即開始讀取或者寫入,而不用等待一個滴答的時間。但是這也意味著速率被限制為N.1個滴答的bufferevent可能傳輸N+1個滴答的通信量。

            滴答不能小于1毫秒,毫秒的小數部分都被忽略。

            bufferevent和SSL

            bufferevent可以使用OpenSSL庫實現SSL/TLS安全傳輸層。因為很多應用不需要或者不想鏈接OpenSSL,這部分功能在單獨的libevent_openssl庫中實現。未來版本的libevent可能會添加其他SSL/TLS庫,如NSS或者GnuTLS,但是當前只有OpenSSL。

            OpenSSL功能在2.0.3-alpha版本引入,然而直到2.0.5-beta和2.0.6-rc版本才能良好工作。

            這一節不包含對OpenSSL、SSL/TLS或者密碼學的概述。

            這一節描述的函數都在event2/bufferevent_ssl.h中聲明。

            4.1 創建和使用基于OpenSSL的bufferevent

            接口

            enum bufferevent_ssl_state {
                    BUFFEREVENT_SSL_OPEN 
            = 0,
                    BUFFEREVENT_SSL_CONNECTING 
            = 1,
                    BUFFEREVENT_SSL_ACCEPTING 
            = 2
            };

            struct bufferevent *
            bufferevent_openssl_filter_new(
            struct event_base *base,
                
            struct bufferevent *underlying,
                SSL 
            *ssl,
                
            enum bufferevent_ssl_state state,
                
            int options);

            struct bufferevent *
            bufferevent_openssl_socket_new(
            struct event_base *base,
                evutil_socket_t fd,
                SSL 
            *ssl,
                
            enum bufferevent_ssl_state state,
                
            int options);

            可以創建兩種類型的SSL bufferevent:基于過濾器的、在另一個底層bufferevent之上進行通信的buffervent;或者基于套接字的、直接使用OpenSSL進行網絡通信的bufferevent。這兩種bufferevent都要求提供SSL對象及其狀態描述。如果SSL當前作為客戶端在進行協商,狀態應該是BUFFEREVENT_SSL_CONNECTING;如果作為服務器在進行協商,則是BUFFEREVENT_SSL_ACCEPTING;如果SSL握手已經完成,則狀態是BUFFEREVENT_SSL_OPEN。

            接受通常的選項。BEV_OPT_CLOSE_ON_FREE表示在關閉openssl bufferevent對象的時候同時關閉SSL對象和底層fd或者bufferevent。

            創建基于套接字的bufferevent時,如果SSL對象已經設置了套接字,就不需要提供套接字了:只要傳遞-1就可以。也可以隨后調用bufferevent_setfd()來設置。

            接口

            SSL *bufferevent_openssl_get_ssl(struct bufferevent *bev);

            這個函數返回OpenSSL bufferevent使用的SSL對象。如果bev不是一個基于OpenSSL的bufferevent,則返回NULL。

            接口

            unsigned long bufferevent_get_openssl_error(struct bufferevent *bev);

            這個函數返回給定bufferevent的第一個未決的OpenSSL錯誤;如果沒有未決的錯誤,則返回0。錯誤值的格式與openssl庫中的ERR_get_error()返回的相同。

            接口

            int bufferevent_ssl_renegotiate(struct bufferevent *bev);
            調用這個函數要求SSL重新協商,bufferevent會調用合適的回調函數。這是個高級功能,通常應該避免使用,除非你確實知道自己在做什么,特別是有些SSL版本具有與重新協商相關的安全問題。

            无码精品久久久久久人妻中字| 久久久久成人精品无码中文字幕| 久久精品国产亚洲AV嫖农村妇女| 久久精品女人天堂AV麻| 99久久中文字幕| 成人久久精品一区二区三区| 中文精品久久久久人妻不卡| 久久这里有精品| 免费精品国产日韩热久久| 女同久久| 日韩精品久久久久久久电影| 伊人热热久久原色播放www| 久久精品国产精品亚洲| 人人狠狠综合久久亚洲| 久久人人爽人人澡人人高潮AV| 久久99精品久久久久久野外| 精品国产热久久久福利| 久久亚洲高清综合| 亚洲一区精品伊人久久伊人 | 91精品国产91久久| 久久国产一区二区| 国内精品久久久久久久久| 精品久久久久久无码人妻热| 久久伊人五月天论坛| 色老头网站久久网| 久久精品人人做人人爽电影蜜月 | 久久婷婷成人综合色综合| 无码人妻久久久一区二区三区| 久久精品人人做人人妻人人玩 | 日韩av无码久久精品免费| 99久久婷婷国产综合亚洲| 精品人妻伦九区久久AAA片69| 一本一道久久a久久精品综合| 久久香蕉国产线看观看精品yw| 久久久久综合网久久| 亚洲国产成人精品无码久久久久久综合| 欧美性大战久久久久久| 久久久久久亚洲Av无码精品专口| 97久久精品人人做人人爽| 久久99热这里只频精品6| 99久久精品国产麻豆|