• <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/Ref7_evbuffer.html

            libevent的evbuffer實現(xiàn)了為向后面添加數(shù)據(jù)和從前面移除數(shù)據(jù)而優(yōu)化的字節(jié)隊列。

            evbuffer用于處理緩沖網(wǎng)絡(luò)IO的“緩沖”部分。它不提供調(diào)度IO或者當(dāng)IO就緒時觸發(fā)IO的功能:這是bufferevent的工作。

            除非特別說明,本章描述的函數(shù)都在event2/buffer.h中聲明。

            創(chuàng)建和釋放evbuffer

            接口
            struct evbuffer *evbuffer_new(void);
            void evbuffer_free(struct evbuffer *buf);

            這兩個函數(shù)的功能很簡明:evbuffer_new()分配和返回一個新的空evbuffer;而evbuffer_free()釋放evbuffer和其內(nèi)容。

            這兩個函數(shù)從libevent 0.8版就存在了。

            evbuffer與線程安全

            接口

            int evbuffer_enable_locking(struct evbuffer *buf, void *lock);
            void evbuffer_lock(struct evbuffer *buf);
            void evbuffer_unlock(struct evbuffer *buf);

            默認(rèn)情況下,在多個線程中同時訪問evbuffer是不安全的。如果需要這樣的訪問,可以調(diào)用evbuffer_enable_locking()。如果lock參數(shù)為NULL,libevent會使用evthread_set_lock_creation_callback提供的鎖創(chuàng)建函數(shù)創(chuàng)建一個鎖。否則,libevent將lock參數(shù)用作鎖。

            evbuffer_lock()和evbuffer_unlock()函數(shù)分別請求和釋放evbuffer上的鎖。可以使用這兩個函數(shù)讓一系列操作是原子的。如果evbuffer沒有啟用鎖,這兩個函數(shù)不做任何操作。

            (注意:對于單個操作,不需要調(diào)用evbuffer_lock()和evbuffer_unlock():如果evbuffer啟用了鎖,單個操作就已經(jīng)是原子的。只有在需要多個操作連續(xù)執(zhí)行,不讓其他線程介入的時候,才需要手動鎖定evbuffer)

            這些函數(shù)都在2.0.1-alpha版本中引入。

            檢查evbuffer

            接口

            size_t evbuffer_get_length(const struct evbuffer *buf);

            這個函數(shù)返回evbuffer存儲的字節(jié)數(shù),它在2.0.1-alpha版本中引入。

            接口

            size_t evbuffer_get_contiguous_space(const struct evbuffer *buf);

            這個函數(shù)返回連續(xù)地存儲在evbuffer前面的字節(jié)數(shù)。evbuffer中的數(shù)據(jù)可能存儲在多個分隔開的內(nèi)存塊中,這個函數(shù)返回當(dāng)前第一個塊中的字節(jié)數(shù)。

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

            向evbuffer添加數(shù)據(jù):基礎(chǔ)

            接口

            int evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen);

            這個函數(shù)添加data處的datalen字節(jié)到buf的末尾,成功時返回0,失敗時返回-1。

            接口

            int evbuffer_add_printf(struct evbuffer *buf, const char *fmt, )
            int evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap);

            這些函數(shù)添加格式化的數(shù)據(jù)到buf末尾。格式參數(shù)和其他參數(shù)的處理分別與C庫函數(shù)printf和vprintf相同。函數(shù)返回添加的字節(jié)數(shù)。

            接口

            int evbuffer_expand(struct evbuffer *buf, size_t datlen);

            這個函數(shù)修改緩沖區(qū)的最后一塊,或者添加一個新的塊,使得緩沖區(qū)足以容納datlen字節(jié),而不需要更多的內(nèi)存分配。

            示例

            /* Here are two ways to add "Hello world 2.0.1" to a buffer. */
            /* Directly: */
            evbuffer_add(buf, 
            "Hello world 2.0.1"17);

            /* Via printf: */
            evbuffer_add_printf(buf, 
            "Hello %s %d.%d.%d""world"201);

            evbuffer_add()和evbuffer_add_printf()函數(shù)在libevent 0.8版本引入;evbuffer_expand()首次出現(xiàn)在0.9版本,而evbuffer_add_printf()首次出現(xiàn)在1.1版本。

            將數(shù)據(jù)從一個evbuffer移動到另一個

            為提高效率,libevent具有將數(shù)據(jù)從一個evbuffer移動到另一個的優(yōu)化函數(shù)。

            接口

            int evbuffer_add_buffer(struct evbuffer *dst, struct evbuffer *src);
            int evbuffer_remove_buffer(struct evbuffer *src, struct evbuffer *dst,
                size_t datlen);

            evbuffer_add_buffer()將src中的所有數(shù)據(jù)移動到dst末尾,成功時返回0,失敗時返回-1。

            evbuffer_remove_buffer()函數(shù)從src中移動datlen字節(jié)到dst末尾,盡量少進(jìn)行復(fù)制。如果字節(jié)數(shù)小于datlen,所有字節(jié)被移動。函數(shù)返回移動的字節(jié)數(shù)。

            evbuffer_add_buffer()在0.8版本引入;evbuffer_remove_buffer()是2.0.1-alpha版本新增加的。

            添加數(shù)據(jù)到evbuffer前面

            接口

            int evbuffer_prepend(struct evbuffer *buf, const void *data, size_t size);
            int evbuffer_prepend_buffer(struct evbuffer *dst, struct evbuffer* src);

            除了將數(shù)據(jù)移動到目標(biāo)緩沖區(qū)前面之外,這兩個函數(shù)的行為分別與evbuffer_add()和evbuffer_add_buffer()相同。

            使用這些函數(shù)時要當(dāng)心,永遠(yuǎn)不要對與bufferevent共享的evbuffer使用。這些函數(shù)是2.0.1-alpha版本新添加的。

            重新排列evbuffer的內(nèi)部布局

            有時候需要取出evbuffer前面的N字節(jié),將其看作連續(xù)的字節(jié)數(shù)組。要做到這一點,首先必須確保緩沖區(qū)的前面確實是連續(xù)的。

            接口

            unsigned char *evbuffer_pullup(struct evbuffer *buf, ev_ssize_t size);

            evbuffer_pullup()函數(shù)“線性化”buf前面的size字節(jié),必要時將進(jìn)行復(fù)制或者移動,以保證這些字節(jié)是連續(xù)的,占據(jù)相同的內(nèi)存塊。如果size是負(fù)的,函數(shù)會線性化整個緩沖區(qū)。如果size大于緩沖區(qū)中的字節(jié)數(shù),函數(shù)返回NULL。否則,evbuffer_pullup()返回指向buf中首字節(jié)的指針。

            調(diào)用evbuffer_pullup()時使用較大的size參數(shù)可能會非常慢,因為這可能需要復(fù)制整個緩沖區(qū)的內(nèi)容。

            示例

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

            #include 
            <string.h>

            int parse_socks4(struct evbuffer *buf, ev_uint16_t *port, ev_uint32_t *addr)
            {
                
            /* Let's parse the start of a SOCKS4 request!  The format is easy:
                 * 1 byte of version, 1 byte of command, 2 bytes destport, 4 bytes of
                 * destip. 
            */
                unsigned 
            char *mem;

                mem 
            = evbuffer_pullup(buf, 8);

                
            if (mem == NULL) {
                    
            /* Not enough data in the buffer */
                    
            return 0;
                } 
            else if (mem[0!= 4 || mem[1!= 1) {
                    
            /* Unrecognized protocol or command */
                    
            return -1;
                } 
            else {
                    memcpy(port, mem
            +22);
                    memcpy(addr, mem
            +44);
                    
            *port = ntohs(*port);
                    
            *addr = ntohl(*addr);
                    
            /* Actually remove the data from the buffer now that we know we
                       like it. 
            */
                    evbuffer_drain(buf, 
            8);
                    
            return 1;
                }
            }

            提示

            使用evbuffer_get_contiguous_space()返回的值作為尺寸值調(diào)用evbuffer_pullup()不會導(dǎo)致任何數(shù)據(jù)復(fù)制或者移動。

            evbuffer_pullup()函數(shù)由2.0.1-alpha版本新增加:先前版本的libevent總是保證evbuffer中的數(shù)據(jù)是連續(xù)的,而不計開銷。

            從evbuffer中移除數(shù)據(jù)

            接口

            int evbuffer_drain(struct evbuffer *buf, size_t len);
            int evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen);

            evbuffer_remove()函數(shù)從buf前面復(fù)制和移除datlen字節(jié)到data處的內(nèi)存中。如果可用字節(jié)少于datlen,函數(shù)復(fù)制所有字節(jié)。失敗時返回-1,否則返回復(fù)制了的字節(jié)數(shù)。

            evbuffer_drain()函數(shù)的行為與evbuffer_remove()相同,只是它不進(jìn)行數(shù)據(jù)復(fù)制:而只是將數(shù)據(jù)從緩沖區(qū)前面移除。成功時返回0,失敗時返回-1。

            evbuffer_drain()由0.8版引入,evbuffer_remove()首次出現(xiàn)在0.9版。

            從evbuffer中復(fù)制出數(shù)據(jù)

            有時候需要獲取緩沖區(qū)前面數(shù)據(jù)的副本,而不清除數(shù)據(jù)。比如說,可能需要查看某特定類型的記錄是否已經(jīng)完整到達(dá),而不清除任何數(shù)據(jù)(像evbuffer_remove那樣),或者在內(nèi)部重新排列緩沖區(qū)(像evbuffer_pullup那樣)。

            接口

            ev_ssize_t evbuffer_copyout(struct evbuffer *buf, void *data, size_t datlen);
            ev_ssize_t evbuffer_copyout_from(
            struct evbuffer *buf,
                 
            const struct evbuffer_ptr *pos,
                 
            void *data_out, size_t datlen);

            evbuffer_copyout()的行為與evbuffer_remove()相同,但是它不從緩沖區(qū)移除任何數(shù)據(jù)。也就是說,它從buf前面復(fù)制datlen字節(jié)到data處的內(nèi)存中。如果可用字節(jié)少于datlen,函數(shù)會復(fù)制所有字節(jié)。失敗時返回-1,否則返回復(fù)制的字節(jié)數(shù)。

            如果從緩沖區(qū)復(fù)制數(shù)據(jù)太慢,可以使用evbuffer_peek()。

            示例

            #include <event2/buffer.h>
            #include 
            <event2/util.h>
            #include 
            <stdlib.h>
            #include 
            <stdlib.h>

            int get_record(struct evbuffer *buf, size_t *size_out, char **record_out)
            {
                
            /* Let's assume that we're speaking some protocol where records
                   contain a 4-byte size field in network order, followed by that
                   number of bytes.  We will return 1 and set the 'out' fields if we
                   have a whole record, return 0 if the record isn't here yet, and
                   -1 on error.  
            */
                size_t buffer_len 
            = evbuffer_get_length(buf);
                ev_uint32_t record_len;
                
            char *record;

                
            if (buffer_len < 4)
                   
            return 0/* The size field hasn't arrived. */

               
            /* We use evbuffer_copyout here so that the size field will stay on
                   the buffer for now. 
            */
                evbuffer_copyout(buf, 
            &record_len, 4);
                
            /* Convert len_buf into host order. */
                record_len 
            = ntohl(record_len);
                
            if (buffer_len < record_len + 4)
                    
            return 0/* The record hasn't arrived */

                
            /* Okay, _now_ we can remove the record. */
                record 
            = malloc(record_len);
                
            if (record == NULL)
                    
            return -1;

                evbuffer_drain(buf, 
            4);
                evbuffer_remove(buf, record, record_len);

                
            *record_out = record;
                
            *size_out = record_len;
                
            return 1;
            }

            10 面向行的輸入

            接口

            enum evbuffer_eol_style {
                    EVBUFFER_EOL_ANY,
                    EVBUFFER_EOL_CRLF,
                    EVBUFFER_EOL_CRLF_STRICT,
                    EVBUFFER_EOL_LF,
                    EVBUFFER_EOL_NUL
            };
            char *evbuffer_readln(struct evbuffer *buffer, size_t *n_read_out,
                
            enum evbuffer_eol_style eol_style);

            很多互聯(lián)網(wǎng)協(xié)議使用基于行的格式。evbuffer_readln()函數(shù)從evbuffer前面取出一行,用一個新分配的空字符結(jié)束的字符串返回這一行。如果n_read_out不是NULL,則它被設(shè)置為返回的字符串的字節(jié)數(shù)。如果沒有整行供讀取,函數(shù)返回空。返回的字符串不包括行結(jié)束符。

            evbuffer_readln()理解4種行結(jié)束格式:

            EVBUFFER_EOL_LF

            行尾是單個換行符(也就是\n,ASCII值是0x0A)

            EVBUFFER_EOL_CRLF_STRICT

            行尾是一個回車符,后隨一個換行符(也就是\r\n,ASCII值是0x0D 0x0A)

            EVBUFFER_EOL_CRLF

            行尾是一個可選的回車,后隨一個換行符(也就是說,可以是\r\n或者\n)。這種格式對于解析基于文本的互聯(lián)網(wǎng)協(xié)議很有用,因為標(biāo)準(zhǔn)通常要求\r\n的行結(jié)束符,而不遵循標(biāo)準(zhǔn)的客戶端有時候只使用\n。

            EVBUFFER_EOL_ANY

            行尾是任意數(shù)量、任意次序的回車和換行符。這種格式不是特別有用。它的存在主要是為了向后兼容。

            (注意,如果使用event_se_mem_functions()覆蓋默認(rèn)的malloc,則evbuffer_readln返回的字符串將由你指定的malloc替代函數(shù)分配)

            示例

            char *request_line;
            size_t len;

            request_line 
            = evbuffer_readln(buf, &len, EVBUFFER_EOL_CRLF);
            if (!request_line) {
                
            /* The first line has not arrived yet. */
            else {
                
            if (!strncmp(request_line, "HTTP/1.0 "9)) {
                    
            /* HTTP 1.0 detected  */
                }
                free(request_line);
            }

            evbuffer_readln()接口在1.4.14-stable及以后版本中可用。

            11 在evbuffer中搜索

            evbuffer_ptr結(jié)構(gòu)體指示evbuffer中的一個位置,包含可用于在evbuffer中迭代的數(shù)據(jù)。

            接口

            struct evbuffer_ptr {
                    ev_ssize_t pos;
                    
            struct {
                            
            /* internal fields */
                    } _internal;
            };

            pos是唯一的公有字段,用戶代碼不應(yīng)該使用其他字段。pos指示evbuffer中的一個位置,以到開始處的偏移量表示。

            接口

            struct evbuffer_ptr evbuffer_search(struct evbuffer *buffer,
                
            const char *what, size_t len, const struct evbuffer_ptr *start);
            struct evbuffer_ptr evbuffer_search_range(struct evbuffer *buffer,
                
            const char *what, size_t len, const struct evbuffer_ptr *start,
                
            const struct evbuffer_ptr *end);
            struct evbuffer_ptr evbuffer_search_eol(struct evbuffer *buffer,
                
            struct evbuffer_ptr *start, size_t *eol_len_out,
                
            enum evbuffer_eol_style eol_style);

            evbuffer_search()函數(shù)在緩沖區(qū)中查找含有l(wèi)en個字符的字符串what。函數(shù)返回包含字符串位置,或者在沒有找到字符串時包含-1的evbuffer_ptr結(jié)構(gòu)體。如果提供了start參數(shù),則從指定的位置開始搜索;否則,從開始處進(jìn)行搜索。

            evbuffer_search_range()函數(shù)和evbuffer_search行為相同,只是它只考慮在end之前出現(xiàn)的what。

            evbuffer_search_eol()函數(shù)像evbuffer_readln()一樣檢測行結(jié)束,但是不復(fù)制行,而是返回指向行結(jié)束符的evbuffer_ptr。如果eol_len_out非空,則它被設(shè)置為EOL字符串長度。

            接口

            enum evbuffer_ptr_how {
                    EVBUFFER_PTR_SET,
                    EVBUFFER_PTR_ADD
            };
            int evbuffer_ptr_set(struct evbuffer *buffer, struct evbuffer_ptr *pos,
                size_t position, 
            enum evbuffer_ptr_how how);

            evbuffer_ptr_set函數(shù)操作buffer中的位置pos。如果how等于EVBUFFER_PTR_SET,指針被移動到緩沖區(qū)中的絕對位置position;如果等于EVBUFFER_PTR_ADD,則向前移動position字節(jié)。成功時函數(shù)返回0,失敗時返回-1。

            示例

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

            /* Count the total occurrences of 'str' in 'buf'. */
            int count_instances(struct evbuffer *buf, const char *str)
            {
                size_t len 
            = strlen(str);
                
            int total = 0;
                
            struct evbuffer_ptr p;

                
            if (!len)
                    
            /* Don't try to count the occurrences of a 0-length string. */
                    
            return -1;

                evbuffer_ptr_set(buf, 
            &p, 0, EVBUFFER_PTR_SET);

                
            while (1) {
                     p 
            = evbuffer_search(buf, str, len, &p);
                     
            if (p.pos < 0)
                         
            break;
                     total
            ++;
                     evbuffer_ptr_set(buf, 
            &p, 1, EVBUFFER_PTR_ADD);
                }

                
            return total;
            }

            警告

            任何修改evbuffer或者其布局的調(diào)用都會使得evbuffer_ptr失效,不能再安全地使用。

            這些接口是2.0.1-alpha版本新增加的。

            12 檢測數(shù)據(jù)而不復(fù)制

            有時候需要讀取evbuffer中的數(shù)據(jù)而不進(jìn)行復(fù)制(像evbuffer_copyout()那樣),也不重新排列內(nèi)部內(nèi)存布局(像evbuffer_pullup()那樣)。有時候可能需要查看evbuffer中間的數(shù)據(jù)。

            接口

            struct evbuffer_iovec {
                    
            void *iov_base;
                    size_t iov_len;
            };

            int evbuffer_peek(struct evbuffer *buffer, ev_ssize_t len,
                
            struct evbuffer_ptr *start_at,
                
            struct evbuffer_iovec *vec_out, int n_vec);

            調(diào)用evbuffer_peek()的時候,通過vec_out給定一個evbuffer_iovec數(shù)組,數(shù)組的長度是n_vec。函數(shù)會讓每個結(jié)構(gòu)體包含指向evbuffer內(nèi)部內(nèi)存塊的指針(iov_base)和塊中數(shù)據(jù)長度。

            如果len小于0,evbuffer_peek()會試圖填充所有evbuffer_iovec結(jié)構(gòu)體。否則,函數(shù)會進(jìn)行填充,直到使用了所有結(jié)構(gòu)體,或者見到len字節(jié)為止。如果函數(shù)可以給出所有請求的數(shù)據(jù),則返回實際使用的結(jié)構(gòu)體個數(shù);否則,函數(shù)返回給出所有請求數(shù)據(jù)所需的結(jié)構(gòu)體個數(shù)。

            如果ptr為NULL,函數(shù)從緩沖區(qū)開始處進(jìn)行搜索。否則,從ptr處開始搜索。

            示例

            {
                
            /* Let's look at the first two chunks of buf, and write them to stderr. */
                
            int n, i;
                
            struct evbuffer_iovec v[2];
                n 
            = evbuffer_peek(buf, -1, NULL, v, 2);
                
            for (i=0; i<n; ++i) { /* There might be less than two chunks available. */
                    fwrite(v[i].iov_base, 
            1, v[i].iov_len, stderr);
                }
            }

            {
                
            /* Let's send the first 4906 bytes to stdout via write. */
                
            int n, i, r;
                
            struct evbuffer_iovec *v;
                size_t written 
            = 0;

                
            /* determine how many chunks we need. */
                n 
            = evbuffer_peek(buf, 4096, NULL, NULL, 0);
                
            /* Allocate space for the chunks.  This would be a good time to use
                   alloca() if you have it. 
            */
                v 
            = malloc(sizeof(struct evbuffer_iovec)*n);
                
            /* Actually fill up v. */
                n 
            = evbuffer_peek(buf, 4096, NULL, v, n);
                
            for (i=0; i<n; ++i) {
                    size_t len 
            = v[i].iov_len;
                    
            if (written + len > 4096)
                        len 
            = 4096 - written;
                    r 
            = write(1 /* stdout */, v[i].iov_base, len);
                    
            if (r<=0)
                        
            break;
                    
            /* We keep track of the bytes written separately; if we don't,
                       we may write more than 4096 bytes if the last chunk puts
                       us over the limit. 
            */
                    written 
            += len;
                }
                free(v);
            }

            {
                
            /* Let's get the first 16K of data after the first occurrence of the
                   string "start\n", and pass it to a consume() function. 
            */
                
            struct evbuffer_ptr ptr;
                
            struct evbuffer_iovec v[1];
                
            const char s[] = "start\n";
                
            int n_written;

                ptr 
            = evbuffer_search(buf, s, strlen(s), NULL);
                
            if (ptr.pos == -1)
                    
            return/* no start string found. */

                
            /* Advance the pointer past the start string. */
                
            if (evbuffer_ptr_set(buf, &ptr, strlen(s), EVBUFFER_PTR_ADD) < 0)
                    
            return/* off the end of the string. */

                
            while (n_written < 16*1024) {
                    
            /* Peek at a single chunk. */
                    
            if (evbuffer_peek(buf, -1&ptr, v, 1< 1)
                        
            break;
                    
            /* Pass the data to some user-defined consume function */
                    consume(v[
            0].iov_base, v[0].iov_len);
                    n_written 
            += v[0].iov_len;

                    
            /* Advance the pointer so we see the next chunk next time. */
                    
            if (evbuffer_ptr_set(buf, &ptr, v[0].iov_len, EVBUFFER_PTR_ADD)<0)
                        
            break;
                }
            }

            注意

            修改evbuffer_iovec所指的數(shù)據(jù)會導(dǎo)致不確定的行為

            如果任何函數(shù)修改了evbuffer,則evbuffer_peek()返回的指針會失效

            如果在多個線程中使用evbuffer,確保在調(diào)用evbuffer_peek()之前使用evbuffer_lock(),在使用完evbuffer_peek()給出的內(nèi)容之后進(jìn)行解鎖

            這個函數(shù)是2.0.2-alpha版本新增加的。

            13 直接向evbuffer添加數(shù)據(jù)

            有時候需要能夠直接向evbuffer添加數(shù)據(jù),而不用先將數(shù)據(jù)寫入到字符數(shù)組中,然后再使用evbuffer_add()進(jìn)行復(fù)制。有一對高級函數(shù)可以完成這種功能:evbuffer_reserve_space()和evbuffer_commit_space()。跟evbuffer_peek()一樣,這兩個函數(shù)使用evbuffer_iovec結(jié)構(gòu)體來提供對evbuffer內(nèi)部內(nèi)存的直接訪問。

            接口

            int evbuffer_reserve_space(struct evbuffer *buf, ev_ssize_t size,
                
            struct evbuffer_iovec *vec, int n_vecs);
            int evbuffer_commit_space(struct evbuffer *buf,
                
            struct evbuffer_iovec *vec, int n_vecs);

            evbuffer_reserve_space()函數(shù)給出evbuffer內(nèi)部空間的指針。函數(shù)會擴(kuò)展緩沖區(qū)以至少提供size字節(jié)的空間。到擴(kuò)展空間的指針,以及其長度,會存儲在通過vec傳遞的向量數(shù)組中,n_vec是數(shù)組的長度。

            n_vec的值必須至少是1。如果只提供一個向量,libevent會確保請求的所有連續(xù)空間都在單個擴(kuò)展區(qū)中,但是這可能要求重新排列緩沖區(qū),或者浪費內(nèi)存。為取得更好的性能,應(yīng)該至少提供2個向量。函數(shù)返回提供請求的空間所需的向量數(shù)。

            寫入到向量中的數(shù)據(jù)不會是緩沖區(qū)的一部分,直到調(diào)用evbuffer_commit_space(),使得寫入的數(shù)據(jù)進(jìn)入緩沖區(qū)。如果需要提交少于請求的空間,可以減小任何evbuffer_iovec結(jié)構(gòu)體的iov_len字段,也可以提供較少的向量。函數(shù)成功時返回0,失敗時返回-1。

            提示和警告

            調(diào)用任何重新排列evbuffer或者向其添加數(shù)據(jù)的函數(shù)都將使從evbuffer_reserve_space()獲取的指針失效。

            當(dāng)前實現(xiàn)中,不論用戶提供多少個向量,evbuffer_reserve_space()從不使用多于兩個。未來版本可能會改變這一點。

            如果在多個線程中使用evbuffer,確保在調(diào)用evbuffer_reserve_space()之前使用evbuffer_lock()進(jìn)行鎖定,然后在提交后解除鎖定。

            示例

            /* Suppose we want to fill a buffer with 2048 bytes of output from a
               generate_data() function, without copying. 
            */
            struct evbuffer_iovec v[2];
            int n, i;
            size_t n_to_add 
            = 2048;

            /* Reserve 2048 bytes.*/
            = evbuffer_reserve_space(buf, n_to_add, v, 2);
            if (n<=0)
               
            return/* Unable to reserve the space for some reason. */

            for (i=0; i<&& n_to_add > 0++i) {
               size_t len 
            = v[i].iov_len;
               
            if (len > n_to_add) /* Don't write more than n_to_add bytes. */
                  len 
            = n_to_add;
               
            if (generate_data(v[i].iov_base, len) < 0) {
                  
            /* If there was a problem during data generation, we can just stop
                     here; no data will be committed to the buffer. 
            */
                  
            return;
               }
               
            /* Set iov_len to the number of bytes we actually wrote, so we
                  don't commit too much. 
            */
               v[i].iov_len 
            = len;
            }

            /* We commit the space here.  Note that we give it 'i' (the number of
               vectors we actually used) rather than 'n' (the number of vectors we
               had available. 
            */
            if (evbuffer_commit_space(buf, v, i) < 0)
               
            return/* Error committing */

            不好的示例

            /* Here are some mistakes you can make with evbuffer_reserve().
               DO NOT IMITATE THIS CODE. 
            */
            struct evbuffer_iovec v[2];

            {
              
            /* Do not use the pointers from evbuffer_reserve_space() after
                 calling any functions that modify the buffer. 
            */
              evbuffer_reserve_space(buf, 
            1024, v, 2);
              evbuffer_add(buf, 
            "X"1);
              
            /* WRONG: This next line won't work if evbuffer_add needed to rearrange
                 the buffer's contents.  It might even crash your program. Instead,
                 you add the data before calling evbuffer_reserve_space. 
            */
              memset(v[
            0].iov_base, 'Y', v[0].iov_len-1);
              evbuffer_commit_space(buf, v, 
            1);
            }

            {
              
            /* Do not modify the iov_base pointers. */
              
            const char *data = "Here is some data";
              evbuffer_reserve_space(buf, strlen(data), v, 
            1);
              
            /* WRONG: The next line will not do what you want.  Instead, you
                 should _copy_ the contents of data into v[0].iov_base. 
            */
              v[
            0].iov_base = (char*) data;
              v[
            0].iov_len = strlen(data);
              
            /* In this case, evbuffer_commit_space might give an error if you're
                 lucky 
            */
              evbuffer_commit_space(buf, v, 
            1);
            }

            這個函數(shù)及其提出的接口從2.0.2-alpha版本就存在了。

            14 使用evbuffer的網(wǎng)絡(luò)IO

            libevent中evbuffer的最常見使用場合是網(wǎng)絡(luò)IO。將evbuffer用于網(wǎng)絡(luò)IO的接口是:

            接口

            int evbuffer_write(struct evbuffer *buffer, evutil_socket_t fd);
            int evbuffer_write_atmost(struct evbuffer *buffer, evutil_socket_t fd,
                    ev_ssize_t howmuch);
            int evbuffer_read(struct evbuffer *buffer, evutil_socket_t fd, int howmuch);

            evbuffer_read()函數(shù)從套接字fd讀取至多howmuch字節(jié)到buffer末尾。成功時函數(shù)返回讀取的字節(jié)數(shù),0表示EOF,失敗時返回-1。注意,錯誤碼可能指示非阻塞操作不能立即成功,應(yīng)該檢查錯誤碼EAGAIN(或者Windows中的WSAWOULDBLOCK)。如果howmuch為負(fù),evbuffer_read()試圖猜測要讀取多少數(shù)據(jù)。

            evbuffer_write_atmost()函數(shù)試圖將buffer前面至多howmuch字節(jié)寫入到套接字fd中。成功時函數(shù)返回寫入的字節(jié)數(shù),失敗時返回-1。跟evbuffer_read()一樣,應(yīng)該檢查錯誤碼,看是真的錯誤,還是僅僅指示非阻塞IO不能立即完成。如果為howmuch給出負(fù)值,函數(shù)會試圖寫入buffer的所有內(nèi)容。

            調(diào)用evbuffer_write()與使用負(fù)的howmuch參數(shù)調(diào)用evbuffer_write_atmost()一樣:函數(shù)會試圖盡量清空buffer的內(nèi)容。

            在Unix中,這些函數(shù)應(yīng)該可以在任何支持read和write的文件描述符上正確工作。在Windows中,僅僅支持套接字。

            注意,如果使用bufferevent,則不需要調(diào)用這些函數(shù),bufferevent的代碼已經(jīng)為你調(diào)用了。

            evbuffer_write_atmost()函數(shù)在2.0.1-alpha版本中引入。

            15 evbuffer和回調(diào)

            evbuffer的用戶常常需要知道什么時候向evbuffer添加了數(shù)據(jù),什么時候移除了數(shù)據(jù)。為支持這個,libevent為evbuffer提高了通用回調(diào)機制。

            接口

            struct evbuffer_cb_info {
                    size_t orig_size;
                    size_t n_added;
                    size_t n_deleted;
            };

            typedef 
            void (*evbuffer_cb_func)(struct evbuffer *buffer,
                
            const struct evbuffer_cb_info *info, void *arg);

            向evbuffer添加數(shù)據(jù),或者從中移除數(shù)據(jù)的時候,回調(diào)函數(shù)會被調(diào)用。函數(shù)收到緩沖區(qū)指針、一個evbuffer_cb_info結(jié)構(gòu)體指針,和用戶提供的參數(shù)。evbuffer_cb_info結(jié)構(gòu)體的orig_size字段指示緩沖區(qū)改變大小前的字節(jié)數(shù),n_added字段指示向緩沖區(qū)添加了多少字節(jié);n_deleted字段指示移除了多少字節(jié)。

            接口

            struct evbuffer_cb_entry;
            struct evbuffer_cb_entry *evbuffer_add_cb(struct evbuffer *buffer,
                evbuffer_cb_func cb, 
            void *cbarg);

            evbuffer_add_cb()函數(shù)為evbuffer添加一個回調(diào)函數(shù),返回一個不透明的指針,隨后可用于代表這個特定的回調(diào)實例。cb參數(shù)是將被調(diào)用的函數(shù),cbarg是用戶提供的將傳給這個函數(shù)的指針。

            可以為單個evbuffer設(shè)置多個回調(diào),添加新的回調(diào)不會移除原來的回調(diào)。

            示例

            #include <event2/buffer.h>
            #include 
            <stdio.h>
            #include 
            <stdlib.h>

            /* Here's a callback that remembers how many bytes we have drained in
               total from the buffer, and prints a dot every time we hit a
               megabyte. 
            */
            struct total_processed {
                size_t n;
            };
            void count_megabytes_cb(struct evbuffer *buffer,
                
            const struct evbuffer_cb_info *info, void *arg)
            {
                
            struct total_processed *tp = arg;
                size_t old_n 
            = tp->n;
                
            int megabytes, i;
                tp
            ->+= info->n_deleted;
                megabytes 
            = ((tp->n) >> 20- (old_n >> 20);
                
            for (i=0; i<megabytes; ++i)
                    putc(
            '.', stdout);
            }

            void operation_with_counted_bytes(void)
            {
                
            struct total_processed *tp = malloc(sizeof(*tp));
                
            struct evbuffer *buf = evbuffer_new();
                tp
            ->= 0;
                evbuffer_add_cb(buf, count_megabytes_cb, tp);

                
            /* Use the evbuffer for a while.  When we're done: */
                evbuffer_free(buf);
                free(tp);
            }

            注意:釋放非空evbuffer不會清空其數(shù)據(jù),釋放evbuffer也不會為回調(diào)釋放用戶提供的數(shù)據(jù)指針。

            如果不想讓緩沖區(qū)上的回調(diào)永遠(yuǎn)激活,可以移除或者禁用回調(diào):

            接口

            int evbuffer_remove_cb_entry(struct evbuffer *buffer,
                
            struct evbuffer_cb_entry *ent);
            int evbuffer_remove_cb(struct evbuffer *buffer, evbuffer_cb_func cb,
                
            void *cbarg);

            #define EVBUFFER_CB_ENABLED 1
            int evbuffer_cb_set_flags(struct evbuffer *buffer,
                                      
            struct evbuffer_cb_entry *cb,
                                      ev_uint32_t flags);
            int evbuffer_cb_clear_flags(struct evbuffer *buffer,
                                      
            struct evbuffer_cb_entry *cb,
                                      ev_uint32_t flags);

            可以通過添加回調(diào)時候的evbuffer_cb_entry來移除回調(diào),也可以通過回調(diào)函數(shù)和參數(shù)指針來移除。成功時函數(shù)返回0,失敗時返回-1。

            evbuffer_cb_set_flags()和evbuffer_cb_clear_flags()函數(shù)分別為回調(diào)函數(shù)設(shè)置或者清除給定的標(biāo)志。當(dāng)前只有一個標(biāo)志是用戶可見的:EVBUFFER_CB_ENABLED。這個標(biāo)志默認(rèn)是打開的。如果清除這個標(biāo)志,對evbuffer的修改不會調(diào)用回調(diào)函數(shù)。

            接口

            int evbuffer_defer_callbacks(struct evbuffer *buffer, struct event_base *base);

            跟bufferevent回調(diào)一樣,可以讓evbuffer回調(diào)不在evbuffer被修改時立即運行,而是延遲到某event_base的事件循環(huán)中執(zhí)行。如果有多個evbuffer,它們的回調(diào)潛在地讓數(shù)據(jù)添加到evbuffer中,或者從中移除,又要避免棧崩潰,延遲回調(diào)是很有用的。

            如果回調(diào)被延遲,則最終執(zhí)行時,它可能是多個操作結(jié)果的總和。

            與bufferevent一樣,evbuffer具有內(nèi)部引用計數(shù)的,所以即使還有未執(zhí)行的延遲回調(diào),釋放evbuffer也是安全的。

            整個回調(diào)系統(tǒng)是2.0.1-alpha版本新引入的。evbuffer_cb_(set|clear)_flags()函數(shù)從2.0.2-alpha版本開始存在。

            16 為基于evbuffer的IO避免數(shù)據(jù)復(fù)制

            真正高速的網(wǎng)絡(luò)編程通常要求盡量少的數(shù)據(jù)復(fù)制,libevent為此提供了一些機制:

            接口

            typedef void (*evbuffer_ref_cleanup_cb)(const void *data,
                size_t datalen, 
            void *extra);

            int evbuffer_add_reference(struct evbuffer *outbuf,
                
            const void *data, size_t datlen,
                evbuffer_ref_cleanup_cb cleanupfn, 
            void *extra);

            這個函數(shù)通過引用向evbuffer末尾添加一段數(shù)據(jù)。不會進(jìn)行復(fù)制:evbuffer只會存儲一個到data處的datlen字節(jié)的指針。因此,在evbuffer使用這個指針期間,必須保持指針是有效的。evbuffer會在不再需要這部分?jǐn)?shù)據(jù)的時候調(diào)用用戶提供的cleanupfn函數(shù),帶有提供的data指針、datlen值和extra指針參數(shù)。函數(shù)成功時返回0,失敗時返回-1。

            示例

            #include <event2/buffer.h>
            #include 
            <stdlib.h>
            #include 
            <string.h>

            /* In this example, we have a bunch of evbuffers that we want to use to
               spool a one-megabyte resource out to the network.  We do this
               without keeping any more copies of the resource in memory than
               necessary. 
            */

            #define HUGE_RESOURCE_SIZE (1024*1024)
            struct huge_resource {
                
            /* We keep a count of the references that exist to this structure,
                   so that we know when we can free it. 
            */
                
            int reference_count;
                
            char data[HUGE_RESOURCE_SIZE];
            };

            struct huge_resource *new_resource(void) {
                
            struct huge_resource *hr = malloc(sizeof(struct huge_resource));
                hr
            ->reference_count = 1;
                
            /* Here we should fill hr->data with something.  In real life,
                   we'd probably load something or do a complex calculation.
                   Here, we'll just fill it with EEs. 
            */
                memset(hr
            ->data, 0xEEsizeof(hr->data));
                
            return hr;
            }

            void free_resource(struct huge_resource *hr) {
                
            --hr->reference_count;
                
            if (hr->reference_count == 0)
                    free(hr);
            }

            static void cleanup(const void *data, size_t len, void *arg) {
                free_resource(arg);
            }

            /* This is the function that actually adds the resource to the
               buffer. 
            */
            void spool_resource_to_evbuffer(struct evbuffer *buf,
                
            struct huge_resource *hr)
            {
                
            ++hr->reference_count;
                evbuffer_add_reference(buf, hr
            ->data, HUGE_RESOURCE_SIZE,
                    cleanup, hr);
            }

            一些操作系統(tǒng)提供了將文件寫入到網(wǎng)絡(luò),而不需要將數(shù)據(jù)復(fù)制到用戶空間的方法。如果存在,可以使用下述接口訪問這種機制:

            接口

            int evbuffer_add_file(struct evbuffer *output, int fd, ev_off_t offset,
                size_t length);

            evbuffer_add_file()要求一個打開的可讀文件描述符fd(注意:不是套接字)。函數(shù)將文件中offset處開始的length字節(jié)添加到output末尾。成功時函數(shù)返回0,失敗時返回-1。

            注意

            在2.0.2-alpha版中,對于使用這種方式添加的數(shù)據(jù)的可靠操作只有:通過evbuffer_write*()將其發(fā)送到網(wǎng)絡(luò)、使用evbuffer_drain()清空數(shù)據(jù),或者使用evbuffer_*_buffer()將其移動到另一個evbuffer中。不能使用evbuffer_remove()取出數(shù)據(jù),使用evbuffer_pullup()進(jìn)行線性化等。

            如果操作系統(tǒng)支持splice()或者sendfile(),則調(diào)用evbuffer_write()時libevent會直接使用這些函數(shù)來將來自fd的數(shù)據(jù)發(fā)送到網(wǎng)絡(luò)中,而根本不將數(shù)據(jù)復(fù)制到用戶內(nèi)存中。如果不存在splice()和sendfile(),但是支持mmap(),libevent將進(jìn)行文件映射,而內(nèi)核將意識到永遠(yuǎn)不需要將數(shù)據(jù)復(fù)制到用戶空間。否則,libevent會將數(shù)據(jù)從磁盤讀取到內(nèi)存。

            清空數(shù)據(jù)或者釋放evbuffer時文件描述符將被關(guān)閉。

            這一節(jié)描述的函數(shù)都在2.0.1-alpha版本中引入。evbuffer_add_referece()則從2.0.2-alpha版本開始存在。

            17 讓evbuffer只能添加或者只能移除

            接口

            int evbuffer_freeze(struct evbuffer *buf, int at_front);
            int evbuffer_unfreeze(struct evbuffer *buf, int at_front);

            可以使用這些函數(shù)暫時禁止修改evbuffer的開頭或者末尾。bufferevent的代碼在內(nèi)部使用這些函數(shù)阻止對輸出緩沖區(qū)頭部,或者輸入緩沖區(qū)尾部的意外修改。

            evbuffer_freeze()函數(shù)是2.0.1-alpha版本引入的。

            18 廢棄的evbuffer函數(shù)

            2.0版對evbuffer接口進(jìn)行了很多修改。在此之前,每個evbuffer實現(xiàn)為一個連續(xù)的內(nèi)存塊,訪問效率是非常低的。

            event.h頭文件用于暴露evbuffer結(jié)構(gòu)體的內(nèi)部,但該結(jié)構(gòu)體已經(jīng)不可用了,因為對于依賴于它們的代碼,1.4和2.0版之間的改動太大了。

            要訪問evbuffer中的字節(jié)數(shù),可以使用EVBUFFER_LENGTH()宏;而實際數(shù)據(jù)可以通過EVBUFFER_DATA()來訪問。這兩個宏都在event2/buffer_compat.h中。然而,請注意:EVBUFFER_DATA(b)只是evbuffer_pullup(b,-1)的別名,其開銷可能非常大。

            其他廢棄的接口有:

            廢棄的接口

            char *evbuffer_readline(struct evbuffer *buffer);
            unsigned 
            char *evbuffer_find(struct evbuffer *buffer,
                
            const unsigned char *what, size_t len);

            evbuffer_readline()函數(shù)的工作方式類似于當(dāng)前的evbuffer_readln(buffer,NULL,EVBUFFER_EOL_ANY)。

            evbuffer_find()將在緩沖區(qū)中搜索字符串的首次出現(xiàn),返回其指針。與evbuffer_search()不同的是,它只能找到第一個字符串。為兼容使用這個函數(shù)的老代碼,這個函數(shù)會線性化到被定位字符串末尾的整個緩沖區(qū)。

            回調(diào)函數(shù)也有不同:

            廢棄的接口
            typedef void (*evbuffer_cb)(struct evbuffer *buffer,
                size_t old_len, size_t new_len, 
            void *arg);
            void evbuffer_setcb(struct evbuffer *buffer, evbuffer_cb cb, void *cbarg);

            evbuffer某時刻只能有一個回調(diào),設(shè)置新的回調(diào)會禁止先前的回調(diào),設(shè)置回調(diào)為NULL是最佳的禁止回調(diào)的方法。

            回調(diào)函數(shù)不使用evbuffer_cb_info結(jié)構(gòu)體,而是使用evbuffer的原長度和新長度。因此,如果old_len大于new_len,數(shù)據(jù)被抽取;如果new_len大于old_len,數(shù)據(jù)被添加。回調(diào)不可能延遲,所以添加和刪除操作不可能合并到單個回調(diào)的執(zhí)行中。

            這里給出的廢棄函數(shù)依然在event2/buffer_compat.h中,依然可用。

            久久本道久久综合伊人| 久久精品国内一区二区三区| 伊人久久综合热线大杳蕉下载| 99久久无色码中文字幕人妻| 伊人久久大香线蕉综合5g| 久久精品国产精品亚洲人人| 国产精品成人无码久久久久久 | 国产精品九九久久免费视频 | 久久超乳爆乳中文字幕| 国产亚洲精品久久久久秋霞| 亚洲人成网站999久久久综合| 久久国产精品波多野结衣AV| 国产福利电影一区二区三区久久老子无码午夜伦不 | 久久精品亚洲欧美日韩久久| 99热精品久久只有精品| 精品久久久久中文字| 久久播电影网| 一级做a爰片久久毛片看看| 久久亚洲精品无码aⅴ大香| 一本久久a久久精品亚洲| 色综合久久综合中文综合网 | 国产精品视频久久久| 久久99热狠狠色精品一区| 国产精品成人99久久久久| 久久久久亚洲av成人无码电影 | 午夜肉伦伦影院久久精品免费看国产一区二区三区 | 亚洲欧洲久久av| 亚洲色大成网站WWW久久九九| 久久99精品国产自在现线小黄鸭| 久久福利青草精品资源站免费| 99久久精品免费看国产一区二区三区| 狠狠综合久久综合中文88| 欧美粉嫩小泬久久久久久久 | 久久精品99无色码中文字幕| 久久国产AVJUST麻豆| 国产成人无码久久久精品一| 久久AⅤ人妻少妇嫩草影院| 久久99热这里只频精品6| 国产精品久久午夜夜伦鲁鲁| 久久精品国产亚洲一区二区三区 | 日本道色综合久久影院|