• <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

            很多時(shí)候,除了響應(yīng)事件之外,應(yīng)用還希望做一定的數(shù)據(jù)緩沖。比如說(shuō),寫(xiě)入數(shù)據(jù)的時(shí)候,通常的運(yùn)行模式是:

            決定要向連接寫(xiě)入一些數(shù)據(jù),把數(shù)據(jù)放入到緩沖區(qū)中

            等待連接可以寫(xiě)入

            寫(xiě)入盡量多的數(shù)據(jù)

            記住寫(xiě)入了多少數(shù)據(jù),如果還有更多數(shù)據(jù)要寫(xiě)入,等待連接再次可以寫(xiě)入

            這種緩沖IO模式很通用,libevent為此提供了一種通用機(jī)制,即bufferevent。bufferevent由一個(gè)底層的傳輸端口(如套接字),一個(gè)讀取緩沖區(qū)和一個(gè)寫(xiě)入緩沖區(qū)組成。與通常的事件在底層傳輸端口已經(jīng)就緒,可以讀取或者寫(xiě)入的時(shí)候執(zhí)行回調(diào)不同的是,bufferevent在讀取或者寫(xiě)入了足夠量的數(shù)據(jù)之后調(diào)用用戶(hù)提供的回調(diào)。

            有多種共享公用接口的bufferevent類(lèi)型,編寫(xiě)本文時(shí)已存在以下類(lèi)型:

            基于套接字的bufferevent:使用event_*接口作為后端,通過(guò)底層流式套接字發(fā)送或者接收數(shù)據(jù)的bufferevent

            異步IO bufferevent:使用Windows IOCP接口,通過(guò)底層流式套接字發(fā)送或者接收數(shù)據(jù)的bufferevent(僅用于Windows,試驗(yàn)中)

            過(guò)濾bufferevent:將數(shù)據(jù)傳輸?shù)降讓觔ufferevent對(duì)象之前,處理輸入或者輸出數(shù)據(jù)的bufferevent:比如說(shuō),為了壓縮或者轉(zhuǎn)換數(shù)據(jù)。

            成對(duì)的bufferevent:相互傳輸數(shù)據(jù)的兩個(gè)bufferevent。

            注意:截止2.0.2-alpha,這里列出的bufferevent接口還沒(méi)有完全正交于所有的bufferevent類(lèi)型。也就是說(shuō),下面將要介紹的接口不是都能用于所有bufferevent類(lèi)型。libevent開(kāi)發(fā)者在未來(lái)版本中將修正這個(gè)問(wèn)題。

            也請(qǐng)注意:當(dāng)前bufferevent只能用于像TCP這樣的面向流的協(xié)議,將來(lái)才可能會(huì)支持像UDP這樣的面向數(shù)據(jù)報(bào)的協(xié)議。

            本節(jié)描述的所有函數(shù)和類(lèi)型都在event2/bufferevent.h中聲明。特別提及的關(guān)于evbuffer的函數(shù)聲明在event2/buffer.h中,詳細(xì)信息請(qǐng)參考下一章。

            bufferevent和evbuffer

            每個(gè)bufferevent都有一個(gè)輸入緩沖區(qū)和一個(gè)輸出緩沖區(qū),它們的類(lèi)型都是“struct evbuffer”。有數(shù)據(jù)要寫(xiě)入到bufferevent時(shí),添加數(shù)據(jù)到輸出緩沖區(qū);bufferevent中有數(shù)據(jù)供讀取的時(shí)候,從輸入緩沖區(qū)抽取(drain)數(shù)據(jù)。

            evbuffer接口支持很多種操作,后面的章節(jié)將討論這些操作。

            回調(diào)和水位

            每個(gè)bufferevent有兩個(gè)數(shù)據(jù)相關(guān)的回調(diào):一個(gè)讀取回調(diào)和一個(gè)寫(xiě)入回調(diào)。默認(rèn)情況下,從底層傳輸端口讀取了任意量的數(shù)據(jù)之后會(huì)調(diào)用讀取回調(diào);輸出緩沖區(qū)中足夠量的數(shù)據(jù)被清空到底層傳輸端口后寫(xiě)入回調(diào)會(huì)被調(diào)用。通過(guò)調(diào)整bufferevent的讀取和寫(xiě)入“水位(watermarks)”可以覆蓋這些函數(shù)的默認(rèn)行為。

            每個(gè)bufferevent有四個(gè)水位:

            讀取低水位:讀取操作使得輸入緩沖區(qū)的數(shù)據(jù)量在此級(jí)別或者更高時(shí),讀取回調(diào)將被調(diào)用。默認(rèn)值為0,所以每個(gè)讀取操作都會(huì)導(dǎo)致讀取回調(diào)被調(diào)用。

            讀取高水位:輸入緩沖區(qū)的數(shù)據(jù)量達(dá)到此級(jí)別后,bufferevent將停止讀取,直到輸入緩沖區(qū)中足夠量的數(shù)據(jù)被抽取,使得數(shù)據(jù)量低于此級(jí)別。默認(rèn)值是無(wú)限,所以永遠(yuǎn)不會(huì)因?yàn)檩斎刖彌_區(qū)的大小而停止讀取。

            寫(xiě)入低水位:寫(xiě)入操作使得輸出緩沖區(qū)的數(shù)據(jù)量達(dá)到或者低于此級(jí)別時(shí),寫(xiě)入回調(diào)將被調(diào)用。默認(rèn)值是0,所以只有輸出緩沖區(qū)空的時(shí)候才會(huì)調(diào)用寫(xiě)入回調(diào)。

            寫(xiě)入高水位:bufferevent沒(méi)有直接使用這個(gè)水位。它在bufferevent用作另外一個(gè)bufferevent的底層傳輸端口時(shí)有特殊意義請(qǐng)看后面關(guān)于過(guò)濾bufferevent的介紹

             

            bufferevent也有“錯(cuò)誤”或者“事件”回調(diào),用于應(yīng)用通知非面向數(shù)據(jù)的事件,如連接已經(jīng)關(guān)閉或者發(fā)生錯(cuò)誤。定義了下列事件標(biāo)志:

            BEV_EVENT_READING:讀取操作時(shí)發(fā)生某事件,具體是哪種事件請(qǐng)看其他標(biāo)志。

            BEV_EVENT_WRITING:寫(xiě)入操作時(shí)發(fā)生某事件,具體是哪種事件請(qǐng)看其他標(biāo)志。

            BEV_EVENT_ERROR:操作時(shí)發(fā)生錯(cuò)誤。關(guān)于錯(cuò)誤的更多信息,請(qǐng)調(diào)用EVUTIL_SOCKET_ERROR()。

            BEV_EVENT_TIMEOUT:發(fā)生超時(shí)。

            BEV_EVENT_EOF:遇到文件結(jié)束指示。

            BEV_EVENT_CONNECTED:請(qǐng)求連接過(guò)程已經(jīng)完成。

            上述標(biāo)志由2.0.2-alpha新引入。

            延遲回調(diào)

            默認(rèn)情況下,bufferevent的回調(diào)在相應(yīng)的條件發(fā)生時(shí)立即被執(zhí)行。(evbuffer的回調(diào)也是這樣的,隨后會(huì)介紹)在依賴(lài)關(guān)系復(fù)雜的情況下,這種立即調(diào)用會(huì)制造麻煩。比如說(shuō),假如某個(gè)回調(diào)在evbuffer A空的時(shí)候向其中移入數(shù)據(jù),而另一個(gè)回調(diào)在evbuffer A滿(mǎn)的時(shí)候從中取出數(shù)據(jù)。這些調(diào)用都是在棧上發(fā)生的,在依賴(lài)關(guān)系足夠復(fù)雜的時(shí)候,有棧溢出的風(fēng)險(xiǎn)。

            要解決此問(wèn)題,可以請(qǐng)求bufferevent(或者evbuffer)延遲其回調(diào)。條件滿(mǎn)足時(shí),延遲回調(diào)不會(huì)立即調(diào)用,而是event_loop()調(diào)用中被排隊(duì),然后在通常的事件回調(diào)之后執(zhí)行。

            (延遲回調(diào)由libevent 2.0.1-alpha引入)

            bufferevent的選項(xiàng)標(biāo)志

            創(chuàng)建bufferevent時(shí)可以使用一個(gè)或者多個(gè)標(biāo)志修改其行為。可識(shí)別的標(biāo)志有:

            BEV_OPT_CLOSE_ON_FREE:釋放bufferevent時(shí)關(guān)閉底層傳輸端口。這將關(guān)閉底層套接字,釋放底層bufferevent等。

            BEV_OPT_THREADSAFE:自動(dòng)為bufferevent分配鎖,這樣就可以安全地在多個(gè)線(xiàn)程中使用bufferevent。

            BEV_OPT_DEFER_CALLBACKS:設(shè)置這個(gè)標(biāo)志時(shí),bufferevent延遲所有回調(diào),如上所述。

            BEV_OPT_UNLOCK_CALLBACKS:默認(rèn)情況下,如果設(shè)置bufferevent為線(xiàn)程安全的,則bufferevent會(huì)在調(diào)用用戶(hù)提供的回調(diào)時(shí)進(jìn)行鎖定。設(shè)置這個(gè)選項(xiàng)會(huì)讓libevent在執(zhí)行回調(diào)的時(shí)候不進(jìn)行鎖定。

            (BEV_OPT_UNLOCK_CALLBACKS由2.0.5-beta引入,其他選項(xiàng)由2.0.1-alpha版引入)

            基于套接字的bufferevent一起工作

            基于套接字的bufferevent是最簡(jiǎn)單的,它使用libevent的底層事件機(jī)制來(lái)檢測(cè)底層網(wǎng)絡(luò)套接字是否已經(jīng)就緒,可以進(jìn)行讀寫(xiě)操作,并且使用底層網(wǎng)絡(luò)調(diào)用(如readv、writev、WSASend、WSARecv)來(lái)發(fā)送和接收數(shù)據(jù)。

            5.1 創(chuàng)建基于套接字的bufferevent

            可以使用bufferevent_socket_new()創(chuàng)建基于套接字的bufferevent。

            接口
            struct bufferevent *bufferevent_socket_new(
                
            struct event_base *base,
                evutil_socket_t fd,
                
            enum bufferevent_options options);

            base是event_base,options是表示bufferevent選項(xiàng)(BEV_OPT_CLOSE_ON_FREE等)的位掩碼,fd是一個(gè)可選的表示套接字的文件描述符。如果想以后設(shè)置文件描述符,可以設(shè)置fd為-1。

            成功時(shí)函數(shù)返回一個(gè)bufferevent,失敗則返回NULL。

            bufferevent_socket_new()函數(shù)由2.0.1-alpha版新引入。

            5.2 在基于套接字的bufferevent上啟動(dòng)連接

            如果bufferevent的套接字還沒(méi)有連接上,可以啟動(dòng)新的連接。

            接口

            int bufferevent_socket_connect(struct bufferevent *bev,
                
            struct sockaddr *address, int addrlen);

            address和addrlen參數(shù)跟標(biāo)準(zhǔn)調(diào)用connect()的參數(shù)相同。如果還沒(méi)有為bufferevent設(shè)置套接字,調(diào)用函數(shù)將為其分配一個(gè)新的流套接字,并且設(shè)置為非阻塞的。

            如果已經(jīng)為bufferevent設(shè)置套接字,調(diào)用bufferevent_socket_connect()將告知libevent套接字還未連接,直到連接成功之前不應(yīng)該對(duì)其進(jìn)行讀取或者寫(xiě)入操作。

            連接完成之前可以向輸出緩沖區(qū)添加數(shù)據(jù)。

            如果連接成功啟動(dòng),函數(shù)返回0;如果發(fā)生錯(cuò)誤則返回-1。

            示例

            #include <event2/event.h>
            #include 
            <event2/bufferevent.h>
            #include 
            <sys/socket.h>
            #include 
            <string.h>

            void eventcb(struct bufferevent *bev, short events, void *ptr)
            {
                
            if (events & BEV_EVENT_CONNECTED) {
                     
            /* We're connected to 127.0.0.1:8080.   Ordinarily we'd do
                        something here, like start reading or writing. 
            */
                } 
            else if (events & BEV_EVENT_ERROR) {
                     
            /* An error occured while connecting. */
                }
            }

            int main_loop(void)
            {
                
            struct event_base *base;
                
            struct bufferevent *bev;
                
            struct sockaddr_in sin;

                
            base = event_base_new();

                memset(
            &sin, 0sizeof(sin));
                sin.sin_family 
            = AF_INET;
                sin.sin_addr.s_addr 
            = htonl(0x7f000001); /* 127.0.0.1 */
                sin.sin_port 
            = htons(8080); /* Port 8080 */

                bev 
            = bufferevent_socket_new(base-1, BEV_OPT_CLOSE_ON_FREE);

                bufferevent_setcb(bev, NULL, NULL, eventcb, NULL);

                
            if (bufferevent_socket_connect(bev,
                    (
            struct sockaddr *)&sin, sizeof(sin)) < 0) {
                    
            /* Error starting connection */
                    bufferevent_free(bev);
                    
            return -1;
                }

                event_base_dispatch(
            base);
                
            return 0;
            }

            bufferevent_socket_connect()函數(shù)由2.0.2-alpha版引入。在此之前,必須自己手動(dòng)在套接字上調(diào)用connect(),連接完成時(shí),bufferevent將報(bào)告寫(xiě)入事件。

            注意:如果使用bufferevent_socket_connect()發(fā)起連接,將只會(huì)收到BEV_EVENT_CONNECTED事件。如果自己調(diào)用connect(),則連接上將被報(bào)告為寫(xiě)入事件。

            這個(gè)函數(shù)在2.0.2-alpha版引入。

             

            5.3 通過(guò)主機(jī)名啟動(dòng)連接

            常常需要將解析主機(jī)名和連接到主機(jī)合并成單個(gè)操作,libevent為此提供了:

            接口

            int bufferevent_socket_connect_hostname(struct bufferevent *bev,
                
            struct evdns_base *dns_base, int family, const char *hostname,
                
            int port);
            int bufferevent_socket_get_dns_error(struct bufferevent *bev);

            這個(gè)函數(shù)解析名字hostname,查找其family類(lèi)型的地址(允許的地址族類(lèi)型有AF_INET,AF_INET6和AF_UNSPEC)如果名字解析失敗,函數(shù)將調(diào)用事件回調(diào),報(bào)告錯(cuò)誤事件。如果解析成功,函數(shù)將啟動(dòng)連接請(qǐng)求,就像bufferevent_socket_connect()一樣。

            dns_base參數(shù)是可選的:如果為NULL,等待名字查找完成期間調(diào)用線(xiàn)程將被阻塞,而這通常不是期望的行為;如果提供dns_base參數(shù),libevent將使用它來(lái)異步地查詢(xún)主機(jī)名。關(guān)于DNS的更多信息,請(qǐng)看第九章。

            跟bufferevent_socket_connect()一樣,函數(shù)告知libevent,bufferevent上現(xiàn)存的套接字還沒(méi)有連接,在名字解析和連接操作成功完成之前,不應(yīng)該對(duì)套接字進(jìn)行讀取或者寫(xiě)入操作。

            函數(shù)返回的錯(cuò)誤可能是DNS主機(jī)名查詢(xún)錯(cuò)誤,可以調(diào)用bufferevent_socket_get_dns_error()來(lái)獲取最近的錯(cuò)誤。返回值0表示沒(méi)有檢測(cè)到DNS錯(cuò)誤。

            示例:簡(jiǎn)單的HTTP v0客戶(hù)端

            /* Don't actually copy this code: it is a poor way to implement an
               HTTP client.  Have a look at evhttp instead.
            */
            #include 
            <event2/dns.h>
            #include 
            <event2/bufferevent.h>
            #include 
            <event2/buffer.h>
            #include 
            <event2/util.h>
            #include 
            <event2/event.h>

            #include 
            <stdio.h>

            void readcb(struct bufferevent *bev, void *ptr)
            {
                
            char buf[1024];
                
            int n;
                
            struct evbuffer *input = bufferevent_get_input(bev);
                
            while ((n = evbuffer_remove(input, buf, sizeof(buf))) > 0) {
                    fwrite(buf, 
            1, n, stdout);
                }
            }

            void eventcb(struct bufferevent *bev, short events, void *ptr)
            {
                
            if (events & BEV_EVENT_CONNECTED) {
                     printf(
            "Connect okay.\n");
                } 
            else if (events & (BEV_EVENT_ERROR|BEV_EVENT_EOF)) {
                     
            struct event_base *base = ptr;
                     
            if (events & BEV_EVENT_ERROR) {
                             
            int err = bufferevent_socket_get_dns_error(bev);
                             
            if (err)
                                     printf(
            "DNS error: %s\n", evutil_gai_strerror(err));
                     }
                     printf(
            "Closing\n");
                     bufferevent_free(bev);
                     event_base_loopexit(
            base, NULL);
                }
            }

            int main(int argc, char **argv)
            {
                
            struct event_base *base;
                
            struct evdns_base *dns_base;
                
            struct bufferevent *bev;

                
            if (argc != 3) {
                    printf(
            "Trivial HTTP 0.x client\n"
                           
            "Syntax: %s [hostname] [resource]\n"
                           
            "Example: %s www.google.com /\n",argv[0],argv[0]);
                    
            return 1;
                }

                
            base = event_base_new();
                dns_base 
            = evdns_base_new(base1);

                bev 
            = bufferevent_socket_new(base-1, BEV_OPT_CLOSE_ON_FREE);
                bufferevent_setcb(bev, readcb, NULL, eventcb, 
            base);
                bufferevent_enable(bev, EV_READ
            |EV_WRITE);
                evbuffer_add_printf(bufferevent_get_output(bev), 
            "GET %s\r\n", argv[2]);
                bufferevent_socket_connect_hostname(
                    bev, dns_base, AF_UNSPEC, argv[
            1], 80);
                event_base_dispatch(
            base);
                
            return 0;
            }

            通用bufferevent操作

            本節(jié)描述的函數(shù)可用于多種bufferevent實(shí)現(xiàn)。

            6.1 釋放bufferevent

            接口

            void bufferevent_free(struct bufferevent *bev);

            這個(gè)函數(shù)釋放bufferevent。bufferevent內(nèi)部具有引用計(jì)數(shù),所以,如果釋放bufferevent時(shí)還有未決的延遲回調(diào),則在回調(diào)完成之前bufferevent不會(huì)被刪除。

            如果設(shè)置了BEV_OPT_CLOSE_ON_FREE標(biāo)志,并且bufferevent有一個(gè)套接字或者底層bufferevent作為其傳輸端口,則釋放bufferevent將關(guān)閉這個(gè)傳輸端口。

            這個(gè)函數(shù)由libevent 0.8版引入。

            6.2 操作回調(diào)、水位和啟用/禁用

            接口

            typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx);
            typedef 
            void (*bufferevent_event_cb)(struct bufferevent *bev,
                
            short events, void *ctx);

            void bufferevent_setcb(struct bufferevent *bufev,
                bufferevent_data_cb readcb, bufferevent_data_cb writecb,
                bufferevent_event_cb eventcb, 
            void *cbarg);

            void bufferevent_getcb(struct bufferevent *bufev,
                bufferevent_data_cb 
            *readcb_ptr,
                bufferevent_data_cb 
            *writecb_ptr,
                bufferevent_event_cb 
            *eventcb_ptr,
                
            void **cbarg_ptr);

            bufferevent_setcb()函數(shù)修改bufferevent的一個(gè)或者多個(gè)回調(diào)。readcb、writecb和eventcb函數(shù)將分別在已經(jīng)讀取足夠的數(shù)據(jù)、已經(jīng)寫(xiě)入足夠的數(shù)據(jù),或者發(fā)生錯(cuò)誤時(shí)被調(diào)用。每個(gè)回調(diào)函數(shù)的第一個(gè)參數(shù)都是發(fā)生了事件的bufferevent,最后一個(gè)參數(shù)都是調(diào)用bufferevent_setcb()時(shí)用戶(hù)提供的cbarg參數(shù):可以通過(guò)它向回調(diào)傳遞數(shù)據(jù)。事件回調(diào)的events參數(shù)是一個(gè)表示事件標(biāo)志的位掩碼:請(qǐng)看前面的“回調(diào)和水位”節(jié)。

            要禁用回調(diào),傳遞NULL而不是回調(diào)函數(shù)。注意:bufferevent的所有回調(diào)函數(shù)共享單個(gè)cbarg,所以修改它將影響所有回調(diào)函數(shù)。

            這個(gè)函數(shù)由1.4.4版引入。類(lèi)型名bufferevent_data_cb和bufferevent_event_cb由2.0.2-alpha版引入。

            接口

            void bufferevent_enable(struct bufferevent *bufev, short events);
            void bufferevent_disable(struct bufferevent *bufev, short events);

            short bufferevent_get_enabled(struct bufferevent *bufev);

            可以啟用或者禁用bufferevent上的EV_READ、EV_WRITE或者EV_READ EV_WRITE事件。沒(méi)有啟用讀取或者寫(xiě)入事件時(shí),bufferevent將不會(huì)試圖進(jìn)行數(shù)據(jù)讀取或者寫(xiě)入。

            沒(méi)有必要輸出緩沖區(qū)空時(shí)禁用寫(xiě)入事件:bufferevent將自動(dòng)停止寫(xiě)入,然后在有數(shù)據(jù)等待寫(xiě)入時(shí)重新開(kāi)始。

            類(lèi)似地,沒(méi)有必要輸入緩沖區(qū)高于高水位時(shí)禁用讀取事件:bufferevent將自動(dòng)停止讀取,然后在有空間用于讀取時(shí)重新開(kāi)始讀取。

            默認(rèn)情況下,新創(chuàng)建的bufferevent的寫(xiě)入是啟用的,但是讀取沒(méi)有啟用。

            可以調(diào)用bufferevent_get_enabled()確定bufferevent上當(dāng)前啟用的事件。

            除了bufferevent_get_enabled()由2.0.3-alpha版引入外,這些函數(shù)都由0.8版引入。

            接口

            void bufferevent_setwatermark(struct bufferevent *bufev, short events,
                size_t lowmark, size_t highmark);

            bufferevent_setwatermark()函數(shù)調(diào)整單個(gè)bufferevent的讀取水位、寫(xiě)入水位,或者同時(shí)調(diào)整二者。(如果events參數(shù)設(shè)置了EV_READ,調(diào)整讀取水位。如果events設(shè)置了EV_WRITE標(biāo)志,調(diào)整寫(xiě)入水位)

            對(duì)于高水位,0表示“無(wú)限”。

            這個(gè)函數(shù)首次出現(xiàn)在1.4.4版。

            示例

            #include <event2/event.h>
            #include 
            <event2/bufferevent.h>
            #include 
            <event2/buffer.h>
            #include 
            <event2/util.h>

            #include 
            <stdlib.h>
            #include 
            <errno.h>
            #include 
            <string.h>

            struct info {
                
            const char *name;
                size_t total_drained;
            };

            void read_callback(struct bufferevent *bev, void *ctx)
            {
                
            struct info *inf = ctx;
                
            struct evbuffer *input = bufferevent_get_input(bev);
                size_t len 
            = evbuffer_get_length(input);
                
            if (len) {
                    inf
            ->total_drained += len;
                    evbuffer_drain(input, len);
                    printf(
            "Drained %lu bytes from %s\n",
                         (unsigned 
            long) len, inf->name);
                }
            }

            void event_callback(struct bufferevent *bev, short events, void *ctx)
            {
                
            struct info *inf = ctx;
                
            struct evbuffer *input = bufferevent_get_input(bev);
                
            int finished = 0;

                
            if (events & BEV_EVENT_EOF) {
                    size_t len 
            = evbuffer_get_length(input);
                    printf(
            "Got a close from %s.  We drained %lu bytes from it, "
                        
            "and have %lu left.\n", inf->name,
                        (unsigned 
            long)inf->total_drained, (unsigned long)len);
                    finished 
            = 1;
                }
                
            if (events & BEV_EVENT_ERROR) {
                    printf(
            "Got an error from %s: %s\n",
                        inf
            ->name, evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()));
                    finished 
            = 1;
                }
                
            if (finished) {
                    free(ctx);
                    bufferevent_free(bev);
                }
            }

            struct bufferevent *setup_bufferevent(void)
            {
                
            struct bufferevent *b1 = NULL;
                
            struct info *info1;

                info1 
            = malloc(sizeof(struct info));
                info1
            ->name = "buffer 1";
                info1
            ->total_drained = 0;

                
            /*  Here we should set up the bufferevent and make sure it gets
                   connected 
            */

                
            /* Trigger the read callback only whenever there is at least 128 bytes
                   of data in the buffer. 
            */
                bufferevent_setwatermark(b1, EV_READ, 
            1280);

                bufferevent_setcb(b1, read_callback, NULL, event_callback, info1);

                bufferevent_enable(b1, EV_READ); 
            /* Start reading. */
                
            return b1;
            }

            6.3 操作bufferevent中的數(shù)據(jù)

            如果只是通過(guò)網(wǎng)絡(luò)讀取或者寫(xiě)入數(shù)據(jù),而不能觀察操作過(guò)程,是沒(méi)什么好處的。bufferevent提供了下列函數(shù)用于觀察要寫(xiě)入或者讀取的數(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.)

            接口

            struct evbuffer *bufferevent_get_input(struct bufferevent *bufev);
            struct evbuffer *bufferevent_get_output(struct bufferevent *bufev);

            這兩個(gè)函數(shù)提供了非常強(qiáng)大的基礎(chǔ):它們分別返回輸入和輸出緩沖區(qū)。關(guān)于可以對(duì)evbuffer類(lèi)型進(jìn)行的所有操作的完整信息,請(qǐng)看下一章。

            如果寫(xiě)入操作因?yàn)閿?shù)據(jù)量太少而停止(或者讀取操作因?yàn)樘鄶?shù)據(jù)而停止),則向輸出緩沖區(qū)添加數(shù)據(jù)(或者從輸入緩沖區(qū)移除數(shù)據(jù))將自動(dòng)重啟操作。

            這些函數(shù)由2.0.1-alpha版引入。

            接口

            int bufferevent_write(struct bufferevent *bufev,
                
            const void *data, size_t size);
            int bufferevent_write_buffer(struct bufferevent *bufev,
                
            struct evbuffer *buf);

            這些函數(shù)向bufferevent的輸出緩沖區(qū)添加數(shù)據(jù)。bufferevent_write()將內(nèi)存中從data處開(kāi)始的size字節(jié)數(shù)據(jù)添加到輸出緩沖區(qū)的末尾。bufferevent_write_buffer()移除buf的所有內(nèi)容,將其放置到輸出緩沖區(qū)的末尾。成功時(shí)這些函數(shù)都返回0,發(fā)生錯(cuò)誤時(shí)則返回-1。

            這些函數(shù)從0.8版就存在了。

            接口

            size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
            int bufferevent_read_buffer(struct bufferevent *bufev,
                
            struct evbuffer *buf);

            這些函數(shù)從bufferevent的輸入緩沖區(qū)移除數(shù)據(jù)。bufferevent_read()至多從輸入緩沖區(qū)移除size字節(jié)的數(shù)據(jù),將其存儲(chǔ)到內(nèi)存中data處。函數(shù)返回實(shí)際移除的字節(jié)數(shù)。bufferevent_read_buffer()函數(shù)抽空輸入緩沖區(qū)的所有內(nèi)容,將其放置到buf中,成功時(shí)返回0,失敗時(shí)返回-1。

            注意,對(duì)于bufferevent_read(),data處的內(nèi)存塊必須有足夠的空間容納size字節(jié)數(shù)據(jù)。

            bufferevent_read()函數(shù)從0.8版就存在了;bufferevnet_read_buffer()由2.0.1-alpha引入。

            示例

            #include <event2/bufferevent.h>
            #include 
            <event2/buffer.h>

            #include 
            <ctype.h>

            void
            read_callback_uppercase(
            struct bufferevent *bev, void *ctx)
            {
                    
            /* This callback removes the data from bev's input buffer 128
                       bytes at a time, uppercases it, and starts sending it
                       back.

                       (Watch out!  In practice, you shouldn't use toupper to implement
                       a network protocol, unless you know for a fact that the current
                       locale is the one you want to be using.)
                     
            */

                    
            char tmp[128];
                    size_t n;
                    
            int i;
                    
            while (1) {
                            n 
            = bufferevent_read(bev, tmp, sizeof(tmp));
                            
            if (n <= 0)
                                    
            break/* No more data. */
                            
            for (i=0; i<n; ++i)
                                    tmp[i] 
            = toupper(tmp[i]);
                            bufferevent_write(bev, tmp, n);
                    }
            }

            struct proxy_info {
                    
            struct bufferevent *other_bev;
            };
            void
            read_callback_proxy(
            struct bufferevent *bev, void *ctx)
            {
                    
            /* You might use a function like this if you're implementing
                       a simple proxy: it will take data from one connection (on
                       bev), and write it to another, copying as little as
                       possible. 
            */
                    
            struct proxy_info *inf = ctx;

                    bufferevent_read_buffer(bev,
                        bufferevent_get_output(inf
            ->other_bev));
            }

            struct count {
                    unsigned 
            long last_fib[2];
            };

            void
            write_callback_fibonacci(
            struct bufferevent *bev, void *ctx)
            {
                    
            /* Here's a callback that adds some Fibonacci numbers to the
                       output buffer of bev.  It stops once we have added 1k of
                       data; once this data is drained, we'll add more. 
            */
                    
            struct count *= ctx;

                    
            struct evbuffer *tmp = evbuffer_new();
                    
            while (evbuffer_get_length(tmp) < 1024) {
                             unsigned 
            long next = c->last_fib[0+ c->last_fib[1];
                             c
            ->last_fib[0= c->last_fib[1];
                             c
            ->last_fib[1= next;

                             evbuffer_add_printf(tmp, 
            "%lu", next);
                    }

                    
            /* Now we add the whole contents of tmp to bev. */
                    bufferevent_write_buffer(bev, tmp);

                    
            /* We don't need tmp any longer. */
                    evbuffer_free(tmp);
            }

            6.4 讀寫(xiě)超時(shí)

            跟其他事件一樣,可以要求在一定量的時(shí)間已經(jīng)流逝,而沒(méi)有成功寫(xiě)入或者讀取數(shù)據(jù)的時(shí)候調(diào)用一個(gè)超時(shí)回調(diào)。

            接口

            void bufferevent_set_timeouts(struct bufferevent *bufev,
                
            const struct timeval *timeout_read, const struct timeval *timeout_write);

            設(shè)置超時(shí)為NULL會(huì)移除超時(shí)回調(diào)。

            試圖讀取數(shù)據(jù)的時(shí)候,如果至少等待了timeout_read秒,則讀取超時(shí)事件將被觸發(fā)。試圖寫(xiě)入數(shù)據(jù)的時(shí)候,如果至少等待了timeout_write秒,則寫(xiě)入超時(shí)事件將被觸發(fā)。

            注意,只有在讀取或者寫(xiě)入的時(shí)候才會(huì)計(jì)算超時(shí)。也就是說(shuō),如果bufferevent的讀取被禁止,或者輸入緩沖區(qū)滿(mǎn)(達(dá)到其高水位),則讀取超時(shí)被禁止。類(lèi)似的,如果寫(xiě)入被禁止,或者沒(méi)有數(shù)據(jù)待寫(xiě)入,則寫(xiě)入超時(shí)被禁止。

            讀取或者寫(xiě)入超時(shí)發(fā)生時(shí),相應(yīng)的讀取或者寫(xiě)入操作被禁止,然后超時(shí)事件回調(diào)被調(diào)用,帶有標(biāo)志BEV_EVENT_TIMEOUT BEV_EVENT_READING或者BEV_EVENT_TIMEOUT BEV_EVENT_WRITING。

            這個(gè)函數(shù)從2.0.1-alpha就存在了,但是直到2.0.4-alpha才對(duì)于各種bufferevent類(lèi)型行為一致。

            6.5 對(duì)bufferevent發(fā)起清空操作

            接口

            int bufferevent_flush(struct bufferevent *bufev,
                
            short iotype, enum bufferevent_flush_mode state)

            清空bufferevent要求bufferevent強(qiáng)制從底層傳輸端口讀取或者寫(xiě)入盡可能多的數(shù)據(jù),而忽略其他可能保持?jǐn)?shù)據(jù)不被寫(xiě)入的限制條件。函數(shù)的細(xì)節(jié)功能依賴(lài)于bufferevent的具體類(lèi)型。

            iotype參數(shù)應(yīng)該是EV_READ、EV_WRITE或者EV_READ EV_WRITE,用于指示應(yīng)該處理讀取、寫(xiě)入,還是二者都處理。state參數(shù)可以是BEV_NORMAL、BEV_FLUSH或者BEV_FINISHED。BEV_FINISHED指示應(yīng)該告知另一端,沒(méi)有更多數(shù)據(jù)需要發(fā)送了;而B(niǎo)EV_NORMAL和BEV_FLUSH的區(qū)別依賴(lài)于具體的bufferevent類(lèi)型。

            失敗時(shí)bufferevent_flush()返回-1,如果沒(méi)有數(shù)據(jù)被清空則返回0,有數(shù)據(jù)被清空則返回1。

            當(dāng)前(2.0.5-beta)僅有一些bufferevent類(lèi)型實(shí)現(xiàn)了bufferevent_flush()。特別是,基于套接字的bufferevent沒(méi)有實(shí)現(xiàn)。

            類(lèi)型特定的bufferevent函數(shù)

            這些bufferevent函數(shù)不能支持所有bufferevent類(lèi)型。

            接口

            int bufferevent_priority_set(struct bufferevent *bufev, int pri);
            int bufferevent_get_priority(struct bufferevent *bufev);

            這個(gè)函數(shù)調(diào)整bufev的優(yōu)先級(jí)為pri。關(guān)于優(yōu)先級(jí)的更多信息請(qǐng)看event_priority_set()。

            成功時(shí)函數(shù)返回0,失敗時(shí)返回-1。這個(gè)函數(shù)僅能用于基于套接字的bufferevent。

            這個(gè)函數(shù)由1.0引入。

            接口

            int bufferevent_setfd(struct bufferevent *bufev, evutil_socket_t fd);
            evutil_socket_t bufferevent_getfd(
            struct bufferevent *bufev);

            這些函數(shù)設(shè)置或者返回基于fd的事件的文件描述符。只有基于套接字的bufferevent支持setfd()。兩個(gè)函數(shù)都在失敗時(shí)返回-1;setfd()成功時(shí)返回0。

            bufferevent_setfd()函數(shù)由1.4.4引入;bufferevent_getfd()函數(shù)由2.0.2-alpha引入。

            接口

            struct event_base *bufferevent_get_base(struct bufferevent *bev);

            這個(gè)函數(shù)返回bufferevent的event_base,由2.0.9-rc引入。

            接口

            struct bufferevent *bufferevent_get_underlying(struct bufferevent *bufev);

            這個(gè)函數(shù)返回作為bufferevent底層傳輸端口的另一個(gè)bufferevent。關(guān)于這種情況,請(qǐng)看關(guān)于過(guò)濾型bufferevent的介紹。

            這個(gè)函數(shù)由2.0.2-alpha引入。

            手動(dòng)鎖定和解鎖

            有時(shí)候需要確保對(duì)bufferevent的一些操作是原子地執(zhí)行的。為此,libevent提供了手動(dòng)鎖定和解鎖bufferevent的函數(shù)。

            接口

            void bufferevent_lock(struct bufferevent *bufev);
            void bufferevent_unlock(struct bufferevent *bufev);

            注意:如果創(chuàng)建bufferevent時(shí)沒(méi)有指定BEV_OPT_THREADSAFE標(biāo)志,或者沒(méi)有激活libevent的線(xiàn)程支持,則鎖定操作是沒(méi)有效果的。

            用這個(gè)函數(shù)鎖定bufferevent將自動(dòng)同時(shí)鎖定相關(guān)聯(lián)的evbuffer。這些函數(shù)是遞歸的:鎖定已經(jīng)持有鎖的bufferevent是安全的。當(dāng)然,對(duì)于每次鎖定都必須進(jìn)行一次解鎖。

            這些函數(shù)由2.0.6-rc引入。

            已廢棄的bufferevent功能

            從1.4到2.0版,bufferevent的后端代碼一直在進(jìn)行修訂。在老的接口中,訪(fǎng)問(wèn)bufferevent結(jié)構(gòu)體的內(nèi)部是很平常的,并且還會(huì)使用依賴(lài)于這種訪(fǎng)問(wèn)的宏。

            更復(fù)雜的是,老的代碼有時(shí)候?qū)?#8220;evbuffer”前綴用于bufferevent功能。

            這里有一個(gè)在2.0版之前使用過(guò)的東西的概要:

            老的函數(shù)定義在event.h中,而不是event2/bufferevent.h

            如果仍然需要訪(fǎng)問(wèn)bufferevent結(jié)構(gòu)體內(nèi)部的某些公有部分,可以包含event2/bufferevent_struct.h。但是不建議這么做:不同版本的Libevent中bufferevent結(jié)構(gòu)體的內(nèi)容可能會(huì)改變。本節(jié)描述的宏和名字只有在包含了event2/bufferevent_compat.h時(shí)才能使用。

            較老版本中用于設(shè)置bufferevent的接口有所不同:

            接口

            struct bufferevent *bufferevent_new(evutil_socket_t fd,
                evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, 
            void *cbarg);
            int bufferevent_base_set(struct event_base *basestruct bufferevent *bufev);

            bufferevent_new()函數(shù)僅僅在已經(jīng)廢棄的“默認(rèn)”event_base上創(chuàng)建一個(gè)套接字bufferevent。調(diào)用bufferevent_base_set()可以調(diào)整套接字bufferevent的event_base。

            較老版本不使用timeval結(jié)構(gòu)體設(shè)置超時(shí),而是使用秒數(shù):

            接口

            void bufferevent_settimeout(struct bufferevent *bufev,
                
            int timeout_read, int timeout_write);
            最后要指出的是,2.0之前版本中的evbuffer實(shí)現(xiàn)是極其低效的,這對(duì)將bufferevent用于高性能應(yīng)用是一個(gè)問(wèn)題。

            Feedback

            # re: 翻譯:libevent參考手冊(cè)第六章:bufferevent:概念和入門(mén) (八) (轉(zhuǎn))[未登錄](méi)  回復(fù)  更多評(píng)論   

            2014-05-31 15:10 by zz
            不錯(cuò),學(xué)習(xí)了
            国产成人久久激情91| 99久久精品费精品国产| 日韩精品久久久久久久电影蜜臀| 色婷婷综合久久久久中文 | 久久国产三级无码一区二区| 久久无码国产| 久久精品aⅴ无码中文字字幕重口 久久精品a亚洲国产v高清不卡 | 久久精品一区二区国产| 国产精品99久久不卡| 国产精品久久久久久久人人看| 日产精品久久久久久久| 青青青国产精品国产精品久久久久| 久久久综合香蕉尹人综合网| 久久人人爽人人爽人人AV| 久久久久人妻一区精品| 精品久久久久久成人AV| 亚洲午夜无码AV毛片久久| 夜夜亚洲天天久久| 热re99久久6国产精品免费| 久久国产精品免费一区| 996久久国产精品线观看| 亚洲va中文字幕无码久久| 人妻无码精品久久亚瑟影视| 国产福利电影一区二区三区久久久久成人精品综合 | 夜夜亚洲天天久久| 狠狠色婷婷久久综合频道日韩 | 国产综合成人久久大片91| www久久久天天com| 亚洲AV无码1区2区久久| 波多野结衣久久一区二区 | 久久久无码精品亚洲日韩蜜臀浪潮 | 要久久爱在线免费观看| 99久久精品国产毛片| 亚洲国产成人久久综合一| 亚洲色欲久久久综合网| 久久亚洲AV无码精品色午夜| 久久婷婷五月综合97色直播 | 69久久精品无码一区二区| 久久婷婷成人综合色综合| 精品国产青草久久久久福利| 久久只有这精品99|