青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

posts - 200, comments - 8, trackbacks - 0, articles - 0

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

libevent的evbuffer實現了為向后面添加數據和從前面移除數據而優化的字節隊列。

evbuffer用于處理緩沖網絡IO的“緩沖”部分。它不提供調度IO或者當IO就緒時觸發IO的功能:這是bufferevent的工作。

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

創建和釋放evbuffer

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

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

這兩個函數從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);

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

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

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

這些函數都在2.0.1-alpha版本中引入。

檢查evbuffer

接口

size_t evbuffer_get_length(const struct evbuffer *buf);

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

接口

size_t evbuffer_get_contiguous_space(const struct evbuffer *buf);

這個函數返回連續地存儲在evbuffer前面的字節數。evbuffer中的數據可能存儲在多個分隔開的內存塊中,這個函數返回當前第一個塊中的字節數。

這個函數在2.0.1-alpha版本引入。

向evbuffer添加數據:基礎

接口

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

這個函數添加data處的datalen字節到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);

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

接口

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

這個函數修改緩沖區的最后一塊,或者添加一個新的塊,使得緩沖區足以容納datlen字節,而不需要更多的內存分配。

示例

/* 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()函數在libevent 0.8版本引入;evbuffer_expand()首次出現在0.9版本,而evbuffer_add_printf()首次出現在1.1版本。

將數據從一個evbuffer移動到另一個

為提高效率,libevent具有將數據從一個evbuffer移動到另一個的優化函數。

接口

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中的所有數據移動到dst末尾,成功時返回0,失敗時返回-1。

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

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

添加數據到evbuffer前面

接口

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

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

使用這些函數時要當心,永遠不要對與bufferevent共享的evbuffer使用。這些函數是2.0.1-alpha版本新添加的。

重新排列evbuffer的內部布局

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

接口

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

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

調用evbuffer_pullup()時使用較大的size參數可能會非常慢,因為這可能需要復制整個緩沖區的內容。

示例

#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()返回的值作為尺寸值調用evbuffer_pullup()不會導致任何數據復制或者移動。

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

從evbuffer中移除數據

接口

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

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

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

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

從evbuffer中復制出數據

有時候需要獲取緩沖區前面數據的副本,而不清除數據。比如說,可能需要查看某特定類型的記錄是否已經完整到達,而不清除任何數據(像evbuffer_remove那樣),或者在內部重新排列緩沖區(像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()相同,但是它不從緩沖區移除任何數據。也就是說,它從buf前面復制datlen字節到data處的內存中。如果可用字節少于datlen,函數會復制所有字節。失敗時返回-1,否則返回復制的字節數。

如果從緩沖區復制數據太慢,可以使用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);

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

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

EVBUFFER_EOL_LF

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

EVBUFFER_EOL_CRLF_STRICT

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

EVBUFFER_EOL_CRLF

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

EVBUFFER_EOL_ANY

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

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

示例

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結構體指示evbuffer中的一個位置,包含可用于在evbuffer中迭代的數據。

接口

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

pos是唯一的公有字段,用戶代碼不應該使用其他字段。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()函數在緩沖區中查找含有len個字符的字符串what。函數返回包含字符串位置,或者在沒有找到字符串時包含-1的evbuffer_ptr結構體。如果提供了start參數,則從指定的位置開始搜索;否則,從開始處進行搜索。

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

evbuffer_search_eol()函數像evbuffer_readln()一樣檢測行結束,但是不復制行,而是返回指向行結束符的evbuffer_ptr。如果eol_len_out非空,則它被設置為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函數操作buffer中的位置pos。如果how等于EVBUFFER_PTR_SET,指針被移動到緩沖區中的絕對位置position;如果等于EVBUFFER_PTR_ADD,則向前移動position字節。成功時函數返回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或者其布局的調用都會使得evbuffer_ptr失效,不能再安全地使用。

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

12 檢測數據而不復制

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

接口

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);

調用evbuffer_peek()的時候,通過vec_out給定一個evbuffer_iovec數組,數組的長度是n_vec。函數會讓每個結構體包含指向evbuffer內部內存塊的指針(iov_base)和塊中數據長度。

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

如果ptr為NULL,函數從緩沖區開始處進行搜索。否則,從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所指的數據會導致不確定的行為

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

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

這個函數是2.0.2-alpha版本新增加的。

13 直接向evbuffer添加數據

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

接口

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()函數給出evbuffer內部空間的指針。函數會擴展緩沖區以至少提供size字節的空間。到擴展空間的指針,以及其長度,會存儲在通過vec傳遞的向量數組中,n_vec是數組的長度。

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

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

提示和警告

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

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

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

示例

/* 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);
}

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

14 使用evbuffer的網絡IO

libevent中evbuffer的最常見使用場合是網絡IO。將evbuffer用于網絡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()函數從套接字fd讀取至多howmuch字節到buffer末尾。成功時函數返回讀取的字節數,0表示EOF,失敗時返回-1。注意,錯誤碼可能指示非阻塞操作不能立即成功,應該檢查錯誤碼EAGAIN(或者Windows中的WSAWOULDBLOCK)。如果howmuch為負,evbuffer_read()試圖猜測要讀取多少數據。

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

調用evbuffer_write()與使用負的howmuch參數調用evbuffer_write_atmost()一樣:函數會試圖盡量清空buffer的內容。

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

注意,如果使用bufferevent,則不需要調用這些函數,bufferevent的代碼已經為你調用了。

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

15 evbuffer和回調

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

接口

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添加數據,或者從中移除數據的時候,回調函數會被調用。函數收到緩沖區指針、一個evbuffer_cb_info結構體指針,和用戶提供的參數。evbuffer_cb_info結構體的orig_size字段指示緩沖區改變大小前的字節數,n_added字段指示向緩沖區添加了多少字節;n_deleted字段指示移除了多少字節。

接口

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

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

可以為單個evbuffer設置多個回調,添加新的回調不會移除原來的回調。

示例

#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不會清空其數據,釋放evbuffer也不會為回調釋放用戶提供的數據指針。

如果不想讓緩沖區上的回調永遠激活,可以移除或者禁用回調:

接口

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);

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

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

接口

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

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

如果回調被延遲,則最終執行時,它可能是多個操作結果的總和。

與bufferevent一樣,evbuffer具有內部引用計數的,所以即使還有未執行的延遲回調,釋放evbuffer也是安全的。

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

16 為基于evbuffer的IO避免數據復制

真正高速的網絡編程通常要求盡量少的數據復制,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);

這個函數通過引用向evbuffer末尾添加一段數據。不會進行復制:evbuffer只會存儲一個到data處的datlen字節的指針。因此,在evbuffer使用這個指針期間,必須保持指針是有效的。evbuffer會在不再需要這部分數據的時候調用用戶提供的cleanupfn函數,帶有提供的data指針、datlen值和extra指針參數。函數成功時返回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);
}

一些操作系統提供了將文件寫入到網絡,而不需要將數據復制到用戶空間的方法。如果存在,可以使用下述接口訪問這種機制:

接口

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

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

注意

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

如果操作系統支持splice()或者sendfile(),則調用evbuffer_write()時libevent會直接使用這些函數來將來自fd的數據發送到網絡中,而根本不將數據復制到用戶內存中。如果不存在splice()和sendfile(),但是支持mmap(),libevent將進行文件映射,而內核將意識到永遠不需要將數據復制到用戶空間。否則,libevent會將數據從磁盤讀取到內存。

清空數據或者釋放evbuffer時文件描述符將被關閉。

這一節描述的函數都在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);

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

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

18 廢棄的evbuffer函數

2.0版對evbuffer接口進行了很多修改。在此之前,每個evbuffer實現為一個連續的內存塊,訪問效率是非常低的。

event.h頭文件用于暴露evbuffer結構體的內部,但該結構體已經不可用了,因為對于依賴于它們的代碼,1.4和2.0版之間的改動太大了。

要訪問evbuffer中的字節數,可以使用EVBUFFER_LENGTH()宏;而實際數據可以通過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()函數的工作方式類似于當前的evbuffer_readln(buffer,NULL,EVBUFFER_EOL_ANY)。

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

回調函數也有不同:

廢棄的接口
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某時刻只能有一個回調,設置新的回調會禁止先前的回調,設置回調為NULL是最佳的禁止回調的方法。

回調函數不使用evbuffer_cb_info結構體,而是使用evbuffer的原長度和新長度。因此,如果old_len大于new_len,數據被抽取;如果new_len大于old_len,數據被添加。回調不可能延遲,所以添加和刪除操作不可能合并到單個回調的執行中。

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

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            久久久久久久91| 国产精品久久久久77777| 欧美国产日韩精品| 免费高清在线视频一区·| 亚洲午夜国产成人av电影男同| 亚洲精品国产精品国自产在线| 在线免费观看日韩欧美| 在线精品国产成人综合| 在线播放一区| 亚洲欧洲精品一区二区| 亚洲欧洲日本国产| 亚洲美女精品成人在线视频| 日韩午夜av| 亚洲黄色大片| 久久综合中文色婷婷| 久久人人97超碰国产公开结果| 久久精品一二三区| 欧美.日韩.国产.一区.二区| 美女福利精品视频| 亚洲日本乱码在线观看| 亚洲伊人网站| 理论片一区二区在线| 欧美激情四色| 国产欧美在线视频| 亚洲激情婷婷| 亚洲综合成人婷婷小说| 久久视频一区| 亚洲欧洲日本一区二区三区| 亚洲综合日本| 美女性感视频久久久| 国产精品久久久久一区二区三区共 | 国产精品成人在线观看| 销魂美女一区二区三区视频在线| 久久精品一区二区| 欧美日韩国产三区| 永久免费视频成人| 亚洲性线免费观看视频成熟| 久久久久久久网| 日韩一级黄色大片| 国模精品一区二区三区| 一区二区动漫| 欧美黑人一区二区三区| 韩日午夜在线资源一区二区| 亚洲天堂免费在线观看视频| 欧美电影在线观看| 久久高清国产| 国产深夜精品| 亚洲一二三区在线| 亚洲精品1234| 久热爱精品视频线路一| 国内自拍视频一区二区三区| 亚洲视频第一页| 亚洲精品国产无天堂网2021| 久久久噜噜噜久久狠狠50岁| 国产视频观看一区| 欧美影视一区| 亚洲一区二区三区在线视频| 欧美日韩国产区| 亚洲视频国产视频| 亚洲激情小视频| 欧美电影在线播放| 亚洲欧洲日韩女同| 欧美激情亚洲| 欧美波霸影院| 亚洲乱码日产精品bd| 欧美成人亚洲成人| 久久一二三国产| 亚洲欧洲免费视频| 亚洲黄色在线| 欧美日本国产视频| 亚洲欧洲一区二区三区| 久久久久九九九九| 香港久久久电影| 欧美特黄一级大片| 午夜激情一区| 欧美一区二区三区在线| 国产在线欧美日韩| 麻豆精品一区二区av白丝在线| 午夜激情综合网| 激情综合五月天| 欧美成在线视频| 欧美美女日韩| 午夜精品亚洲一区二区三区嫩草| 亚洲欧美综合网| 在线播放国产一区中文字幕剧情欧美| 欧美va亚洲va日韩∨a综合色| 欧美.www| 亚洲欧美日韩精品| 午夜亚洲影视| 亚洲人成绝费网站色www| 国产亚洲女人久久久久毛片| 99视频一区二区三区| 亚洲性视频网址| 一区在线播放| 亚洲精品字幕| 国产亚洲va综合人人澡精品| 免费在线亚洲欧美| 欧美午夜一区二区| 免费欧美视频| 国产精品porn| 欧美成人dvd在线视频| 欧美网站在线观看| 欧美一区二区三区四区在线| 欧美在线看片a免费观看| 亚洲精品免费电影| 亚洲在线免费观看| 亚洲精品美女91| 欧美主播一区二区三区| 夜夜爽www精品| 久久精品国产99国产精品| 99国产精品| 久久精品国产99国产精品澳门| 亚洲视频在线一区| 久久免费视频在线观看| 亚洲一区影音先锋| 欧美a级一区| 欧美在线一二三区| 欧美+日本+国产+在线a∨观看| 亚洲小视频在线| 久久综合五月天婷婷伊人| 欧美一区二区三区成人| 欧美剧在线观看| 欧美成人免费播放| 国产一区二区三区在线观看网站 | 亚洲网站在线观看| 亚洲精品乱码久久久久久蜜桃91| 欧美一区二区三区四区在线观看地址 | 亚洲三级影片| 国内精品久久久久久久影视蜜臀| 亚洲精品久久久久中文字幕欢迎你 | 蜜桃av一区| 久久免费少妇高潮久久精品99| 欧美日韩在线影院| 欧美国产视频日韩| 永久免费毛片在线播放不卡| 欧美亚洲一区二区在线观看| 亚洲视频1区| 欧美日本亚洲| 亚洲美女一区| 亚洲一区bb| 国产精品美腿一区在线看| 欧美三级小说| 亚洲人午夜精品免费| 国际精品欧美精品 | 猛男gaygay欧美视频| 久久久久久久网| 黄色成人在线观看| 久久久久欧美精品| 另类春色校园亚洲| 亚洲电影毛片| 欧美阿v一级看视频| 亚洲国产婷婷香蕉久久久久久| 亚洲国产综合在线| 欧美1区免费| 亚洲精品五月天| 亚洲一区二区三区免费在线观看 | 国产一区二区电影在线观看 | 新67194成人永久网站| 久久大香伊蕉在人线观看热2| 国产精品乱码一区二区三区 | 欧美日韩亚洲网| 日韩视频中文| 欧美亚洲专区| 狠狠爱综合网| 欧美激情一区二区三区在线视频观看 | 欧美日韩国产专区| 亚洲午夜小视频| 久久久精品视频成人| 亚洲激情另类| 国产精品久久久久免费a∨大胸| 午夜伦理片一区| 欧美国产国产综合| 亚洲综合首页| 精品成人在线观看| 欧美日韩精品一本二本三本| 欧美亚洲视频| 亚洲国产成人在线| 亚洲欧美日韩国产一区二区| 国产一区在线播放| 欧美大片专区| 性欧美暴力猛交69hd| 亚洲黄色在线看| 亚洲女人天堂av| 亚洲黄色影片| 国产欧美成人| 久久成人免费| 日韩一二在线观看| 欧美成熟视频| 久久精品国产亚洲精品| 亚洲日本va午夜在线影院| 国产精品自拍一区| 欧美精品网站| 美女视频网站黄色亚洲| 亚洲免费一在线| 亚洲国产精品黑人久久久| 久久国产精品99精品国产| 一本色道综合亚洲| 亚洲激情在线| 尤物精品国产第一福利三区 | 欧美日韩123|