• <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ù)據(jù)緩沖。比如說,寫入數(shù)據(jù)的時候,通常的運行模式是:

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

            等待連接可以寫入

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

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

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

            有多種共享公用接口的bufferevent類型,編寫本文時已存在以下類型:

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

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

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

            成對的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中聲明。特別提及的關于evbuffer的函數(shù)聲明在event2/buffer.h中,詳細信息請參考下一章。

            bufferevent和evbuffer

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

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

            回調(diào)和水位

            每個bufferevent有兩個數(shù)據(jù)相關的回調(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有四個水位:

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

            讀取高水位:輸入緩沖區(qū)的數(shù)據(jù)量達到此級別后,bufferevent將停止讀取,直到輸入緩沖區(qū)中足夠量的數(shù)據(jù)被抽取,使得數(shù)據(jù)量低于此級別。默認值是無限,所以永遠不會因為輸入緩沖區(qū)的大小而停止讀取。

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

            寫入高水位:bufferevent沒有直接使用這個水位。它在bufferevent用作另外一個bufferevent的底層傳輸端口時有特殊意義請看后面關于過濾bufferevent的介紹

             

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

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

            BEV_EVENT_WRITING:寫入操作時發(fā)生某事件,具體是哪種事件請看其他標志。

            BEV_EVENT_ERROR:操作時發(fā)生錯誤。關于錯誤的更多信息,請調(diào)用EVUTIL_SOCKET_ERROR()。

            BEV_EVENT_TIMEOUT:發(fā)生超時。

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

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

            上述標志由2.0.2-alpha新引入

            延遲回調(diào)

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

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

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

            bufferevent的選項標志

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

            BEV_OPT_CLOSE_ON_FREE:釋放bufferevent時關閉底層傳輸端口。這將關閉底層套接字,釋放底層bufferevent等。

            BEV_OPT_THREADSAFE:自動為bufferevent分配鎖,這樣就可以安全地在多個線程中使用bufferevent。

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

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

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

            基于套接字的bufferevent一起工作

            基于套接字的bufferevent是最簡單的,它使用libevent的底層事件機制來檢測底層網(wǎng)絡套接字是否已經(jīng)就緒,可以進行讀寫操作,并且使用底層網(wǎng)絡調(diào)用(如readv、writev、WSASend、WSARecv)來發(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選項(BEV_OPT_CLOSE_ON_FREE等)的位掩碼fd是一個可選的表示套接字的文件描述符。如果想以后設置文件描述符,可以設置fd為-1。

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

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

            5.2 在基于套接字的bufferevent上啟動連接

            如果bufferevent的套接字還沒有連接上,可以啟動新的連接。

            接口

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

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

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

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

            如果連接成功啟動,函數(shù)返回0;如果發(fā)生錯誤則返回-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版引入。在此之前,必須自己手動在套接字上調(diào)用connect(),連接完成時,bufferevent將報告寫入事件。

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

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

             

            5.3 通過主機名啟動連接

            常常需要將解析主機名和連接到主機合并成單個操作,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);

            這個函數(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將使用它來異步地查詢主機名。關于DNS的更多信息,請看第九章。

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

            函數(shù)返回的錯誤可能是DNS主機名查詢錯誤,可以調(diào)用bufferevent_socket_get_dns_error()來獲取最近的錯誤。返回值0表示沒有檢測到DNS錯誤。

            示例:簡單的HTTP v0客戶端

            /* 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實現(xiàn)。

            6.1 釋放bufferevent

            接口

            void bufferevent_free(struct bufferevent *bev);

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

            如果設置了BEV_OPT_CLOSE_ON_FREE標志,并且bufferevent有一個套接字或者底層bufferevent作為其傳輸端口,則釋放bufferevent將關閉這個傳輸端口。

            這個函數(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的一個或者多個回調(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版引入。

            接口

            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事件。沒有啟用讀取或者寫入事件時,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版引入。

            接口

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

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

            對于高水位,0表示“無限”。

            這個函數(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ù)

            如果只是通過網(wǎng)絡讀取或者寫入數(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.)

            接口

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

            這兩個函數(shù)提供了非常強大的基礎:它們分別返回輸入和輸出緩沖區(qū)。關于可以對evbuffer類型進行的所有操作的完整信息,請看下一章。

            如果寫入操作因為數(shù)據(jù)量太少而停止(或者讀取操作因為太多數(shù)據(jù)而停止),則向輸出緩沖區(qū)添加數(shù)據(jù)(或者從輸入緩沖區(qū)移除數(shù)據(jù))將自動重啟操作。

            這些函數(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處開始的size字節(jié)數(shù)據(jù)添加到輸出緩沖區(qū)的末尾。bufferevent_write_buffer()移除buf的所有內(nèi)容,將其放置到輸出緩沖區(qū)的末尾。成功時這些函數(shù)都返回0,發(fā)生錯誤時則返回-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ù),將其存儲到內(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引入。

            示例

            #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 讀寫超時

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

            接口

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

            設置超時為NULL移除超時回調(diào)。

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

            注意,只有在讀取或者寫入的時候才會計算超時。也就是說,如果bufferevent的讀取被禁止,或者輸入緩沖區(qū)滿(達到其高水位),則讀取超時被禁止。類似的,如果寫入被禁止,或者沒有數(shù)據(jù)待寫入,則寫入超時被禁止。

            讀取或者寫入超時發(fā)生時,相應的讀取或者寫入操作被禁止,然后超時事件回調(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ā)起清空操作

            接口

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

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

            iotype參數(shù)應該是EV_READ、EV_WRITE或者EV_READ EV_WRITE,用于指示應該處理讀取、寫入,還是二者都處理。state參數(shù)可以是BEV_NORMAL、BEV_FLUSH或者BEV_FINISHED。BEV_FINISHED指示應該告知另一端,沒有更多數(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)。

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

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

            接口

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

            這個函數(shù)調(diào)整bufev的優(yōu)先級為pri。關于優(yōu)先級的更多信息請看event_priority_set()。

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

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

            接口

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

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

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

            接口

            struct event_base *bufferevent_get_base(struct bufferevent *bev);

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

            接口

            struct bufferevent *bufferevent_get_underlying(struct bufferevent *bufev);

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

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

            手動鎖定和解鎖

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

            接口

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

            注意:如果創(chuàng)建bufferevent時沒有指定BEV_OPT_THREADSAFE標志,或者沒有激活libevent的線程支持,則鎖定操作是沒有效果的。

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

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

            已廢棄的bufferevent功能

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

            更復雜的是,老的代碼有時候?qū)?#8220;evbuffer”前綴用于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時才能使用。

            較老版本中用于設置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)廢棄的“默認”event_base上創(chuàng)建一個套接字bufferevent。調(diào)用bufferevent_base_set()可以調(diào)整套接字bufferevent的event_base。

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

            接口

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

            Feedback

            # re: 翻譯:libevent參考手冊第六章:bufferevent:概念和入門 (八) (轉(zhuǎn))[未登錄]  回復  更多評論   

            2014-05-31 15:10 by zz
            不錯,學習了
            狠狠人妻久久久久久综合| 亚洲AⅤ优女AV综合久久久| 国产亚洲精品久久久久秋霞| 中文成人久久久久影院免费观看| 日韩精品无码久久一区二区三| 久久久久国色AV免费观看| 亚洲精品久久久www| 天堂久久天堂AV色综合| 青青青国产精品国产精品久久久久| 国产成人久久精品二区三区| 2021国内久久精品| 久久精品国产一区| 亚洲伊人久久综合影院| 国产精品天天影视久久综合网| 国产免费久久久久久无码| 中文字幕无码久久精品青草| 久久777国产线看观看精品| 思思久久99热免费精品6| 久久精品毛片免费观看| 91精品国产91久久| 久久久久久国产精品无码超碰| 看全色黄大色大片免费久久久| 亚洲AV乱码久久精品蜜桃| 久久久久国产日韩精品网站| 久久免费精品一区二区| 久久久久亚洲av无码专区喷水 | 91久久精品无码一区二区毛片| 青青久久精品国产免费看| 国产日产久久高清欧美一区| 麻豆精品久久久久久久99蜜桃| 久久91这里精品国产2020| 99久久无码一区人妻a黑| 伊人久久大香线蕉亚洲五月天| 日韩十八禁一区二区久久| 久久国产精品久久久| 久久国产精品77777| 亚洲av日韩精品久久久久久a| 亚洲中文字幕久久精品无码喷水| 久久久久久一区国产精品| 亚洲国产天堂久久综合网站| 久久久久一区二区三区|