很多时候为了应对数据IO的“慢“或者其他原因都需要使用数据缓冲区。对于数据缓冲,我们不陌生,但是对于如何实现这个缓冲区,相信很多时候大家都没有考虑过。今天就通过分析libevent的buffer.c源码,看看libevent是如何实现这个缓冲区的。

数据缓冲区buffer是libevent中网络IO操作中最先接触数据的容器。

1. 缓冲区evbuffer结构

 1 struct evbuffer {
2 //存放数据起始位置
3 u_char *buffer;
4
5 //buffer起始地址
6 u_char *orig_buffer;
7
8 //buffer起始地址与数据存放地址的偏移
9 size_t misalign;
10
11 //总共buffer的长度
12 size_t totallen;
13
14 //缓冲区数据长度
15 size_t off;
16
17 //回调函数
18 void (*cb)(struct evbuffer *, size_t, size_t, void *);
19
20 //回调需要的参数
21 void *cbarg;
22 };

 2. evbuffer结构图

 3. ebuffer如何变化

4. 重要的几个函数注释

     1.evbuffer_add

 1 //从data地址开始datlen个字节数据到evbuffer中
2 int
3 evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen)
4 {
5 // 整个buffer
6 //| totallen |
7 //|--------------|-----------|---------------------------|--------|
8 //|misalign(偏移) |off(数据区) |datlen(需要加入的数据长度) |剩余空间 |
9 //
10 size_t need = buf->misalign + buf->off + datlen;
11 size_t oldoff = buf->off;
12
13 //如果need大于了总长度,需要调整扩大
14 if (buf->totallen < need) {
15 //evbuffer调整扩大
16 if (evbuffer_expand(buf, datlen) == -1)
17 return (-1);
18 }
19 //将datlen长度的data数据复制到buffer中。
20 memcpy(buf->buffer + buf->off, data, datlen);
21 //复制成功,数据长度增加
22 buf->off += datlen;
23 //datlen不为0且buf有回调函数,调用回调函数,告知缓存变化
24 if (datlen && buf->cb != NULL)
25 (*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
26
27 return (0);
28 }

2.evbuffer_drain

 1 //drain:使流出;排掉水
2 //从缓冲区中读出len长度数据。
3 void
4 evbuffer_drain(struct evbuffer *buf, size_t len)
5 {
6 //记录当前缓冲区中的数据长度
7 size_t oldoff = buf->off;
8
9 //如果要读出的长度大于数据长度,就读出全部数据
10 if (len >= buf->off) {
11
12 // 整个buffer
13 //| totallen |
14 //|||-------------------------------------------------------------|
15 //|||剩余空间 |
16 //
17
18 //元素个数清零
19 buf->off = 0;
20 //数据缓冲地址前移到最前面的buf起始位置
21 buf->buffer = buf->orig_buffer;
22 //数据偏移置0
23 buf->misalign = 0;
24 goto done;
25 }
26 //如果读出数据不是全部数据
27
28 // 整个buffer
29 //| totallen |
30 //|--------------|-----------|------------------------------------|
31 //| misalign |off(数据区) | 剩余空间 |
32 // | |
33 // \ /
34
35 // 整个buffer
36 //| totallen |
37 //|-----------------|--------|------------------------------------|
38 //| misalign |off | 剩余空间 |
39 //
40
41 //buffer地址前移len
42 buf->buffer += len;
43 //misalign偏移加len
44 buf->misalign += len;
45 //由于读出数据,off减少len个数据
46 buf->off -= len;
47
48 done:
49 //缓冲区数据长度改变,调用回调函数
50 /* Tell someone about changes in this buffer */
51 if (buf->off != oldoff && buf->cb != NULL)
52 (*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
53
54 }

3.evbuffer_align

 1 //buf进行重新排列
2 static void
3 evbuffer_align(struct evbuffer *buf)
4 {
5 // 整个buffer
6 //| totallen |
7 //|--------------|-----------|-------------------------------------------|
8 //|misalign(偏移)|off(数据区)|datlen(需要加入的数据长度),大于totallen |
9 // | |
10 // \ /
11 // 整个buffer
12 //| totallen |
13 //|-----------|---------------------------------------------------------|
14 //|off(数据区) |datlen(需要加入的数据长度),大于totallen |
15
16
17 //缓冲区数据前移
18 //从buf->buffer拷贝off个字节到buf的orig_buffer
19 memmove(buf->orig_buffer, buf->buffer, buf->off);
20
21 //缓冲区数据起始位置变为buf起始位置
22 buf->buffer = buf->orig_buffer;
23
24 //偏移置为0
25 buf->misalign = 0;
26 }

4.evbuffer_expand

 1 /* Expands the available space in the event buffer to at least datlen */
2 //内存扩展
3 int
4 evbuffer_expand(struct evbuffer *buf, size_t datlen)
5 {
6 // 整个buffer
7 //| totallen |
8 //|--------------|-----------|-------------------------------------------|
9 //|misalign(偏移) |off(数据区) |datlen(需要加入的数据长度),大于totallen |
10 //
11
12 //首先判断是否需要扩展
13 size_t need = buf->misalign + buf->off + datlen;
14
15 //如果need小于totallen,无需扩展
16 /* If we can fit all the data, then we don't have to do anything */
17 if (buf->totallen >= need)
18 return (0);
19
20 /*
21 * If the misalignment fulfills our data needs, we just force an
22 * alignment to happen. Afterwards, we have enough space.
23 */
24 //如果偏移大于datlen,
25 if (buf->misalign >= datlen) {
26 //buf进行重新排列
27 evbuffer_align(buf);
28 } else {
29 //偏移小于datlen,数据元素大于totallen,需要重新分配内存
30 void *newbuf;
31 size_t length = buf->totallen;
32
33 //如果length小于256,length设置256
34 if (length < 256)
35 length = 256;
36 //如果length还是小于need,length扩大2倍直到不小于need
37 while (length < need)
38 length <<= 1;
39
40 //如果有偏移,先重新排列
41 if (buf->orig_buffer != buf->buffer)
42 evbuffer_align(buf);
43 //重新分配内存
44 if ((newbuf = realloc(buf->buffer, length)) == NULL)
45 return (-1);
46 //orig_buffer,buffer都赋值为新地址newbuf
47 buf->orig_buffer = buf->buffer = newbuf;
48 //总长度totallen为length
49 buf->totallen = length;
50 }
51
52 return (0);
53 }

 5.所有代码注释

  1 /*
2 * Copyright (c) 2002, 2003 Niels Provos <provos@citi.umich.edu>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #ifdef WIN32
33 #include <winsock2.h>
34 #include <windows.h>
35 #endif
36
37 #ifdef HAVE_VASPRINTF
38 /* If we have vasprintf, we need to define this before we include stdio.h. */
39 #define _GNU_SOURCE
40 #endif
41
42 #include <sys/types.h>
43
44 #ifdef HAVE_SYS_TIME_H
45 #include <sys/time.h>
46 #endif
47
48 #ifdef HAVE_SYS_IOCTL_H
49 #include <sys/ioctl.h>
50 #endif
51
52 #include <assert.h>
53 #include <errno.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #ifdef HAVE_STDARG_H
58 #include <stdarg.h>
59 #endif
60 #ifdef HAVE_UNISTD_H
61 #include <unistd.h>
62 #endif
63
64 #include "event.h"
65 #include "config.h"
66 #include "evutil.h"
67 #include "./log.h"
68
69 //创建evbuffer
70 struct evbuffer *
71 evbuffer_new(void)
72 {
73 struct evbuffer *buffer;
74
75 buffer = calloc(1, sizeof(struct evbuffer));
76
77 return (buffer);
78 }
79
80 //释放evbuffer
81 void
82 evbuffer_free(struct evbuffer *buffer)
83 {
84 if (buffer->orig_buffer != NULL)
85 free(buffer->orig_buffer);
86 free(buffer);
87 }
88
89 /*
90 * This is a destructive add. The data from one buffer moves into
91 * the other buffer.
92 */
93
94 //交换evbuffer
95 #define SWAP(x,y) do { \
96 (x)->buffer = (y)->buffer; \
97 (x)->orig_buffer = (y)->orig_buffer; \
98 (x)->misalign = (y)->misalign; \
99 (x)->totallen = (y)->totallen; \
100 (x)->off = (y)->off; \
101 } while (0)
102
103 //evbuffer数据交换,outhuf与inbuf
104 int
105 evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf)
106 {
107 int res;
108
109 //如果outbuf没有数据元素
110 /* Short cut for better performance */
111 if (outbuf->off == 0) {
112 struct evbuffer tmp;
113 size_t oldoff = inbuf->off;
114
115 //交换缓冲区
116 /* Swap them directly */
117 SWAP(&tmp, outbuf);
118 SWAP(outbuf, inbuf);
119 SWAP(inbuf, &tmp);
120
121 /*
122 * Optimization comes with a price; we need to notify the
123 * buffer if necessary of the changes. oldoff is the amount
124 * of data that we transfered from inbuf to outbuf
125 */
126 //如果现在的数据元素长度不等于以前inbuf中的数据元素长度,并且有回调函数的话,
127 //交换后inbuf调用回调函数,告诉现在数据元素长度已经改变等信息
128 if (inbuf->off != oldoff && inbuf->cb != NULL)
129 (*inbuf->cb)(inbuf, oldoff, inbuf->off, inbuf->cbarg);
130 //原来inbuf数据元素个数不为0,且有回调函数。交换后的outbuf调用回调。
131 if (oldoff && outbuf->cb != NULL)
132 (*outbuf->cb)(outbuf, 0, oldoff, outbuf->cbarg);
133
134 return (0);
135 }
136 //如果原来的outbuf中有数据元素,把inbuf中的数据元素加入进来
137 res = evbuffer_add(outbuf, inbuf->buffer, inbuf->off);
138 if (res == 0) {
139 //res为零,成功将inbuf的数据元素加入到outbuf中来,所以可以将inbuf中的数据全部排出清空。
140 /* We drain the input buffer on success */
141 evbuffer_drain(inbuf, inbuf->off);
142 }
143
144 return (res);
145 }
146
147 //将数据格式化后添加到buf中
148 int
149 evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap)
150 {
151 char *buffer;
152 size_t space;
153 size_t oldoff = buf->off;
154 int sz;
155 va_list aq;
156
157 /* make sure that at least some space is available */
158 //确保至少有一些空间,这里看看有没有64字节容量。
159 evbuffer_expand(buf, 64);
160 for (;;) {
161 size_t used = buf->misalign + buf->off;
162 buffer = (char *)buf->buffer + buf->off;
163 assert(buf->totallen >= used);
164 space = buf->totallen - used;
165
166 #ifndef va_copy
167 #define va_copy(dst, src) memcpy(&(dst), &(src), sizeof(va_list))
168 #endif
169 va_copy(aq, ap);
170 //返回写入buffer后面的字节数
171 sz = evutil_vsnprintf(buffer, space, fmt, aq);
172
173 va_end(aq);
174
175 if (sz < 0)
176 return (-1);
177 //如果格式化的数据字节数小于剩余的容量
178 if ((size_t)sz < space) {
179 buf->off += sz;
180 if (buf->cb != NULL)
181 (*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
182 return (sz);
183 }
184 //到这边说明容量不够,需要调整
185 if (evbuffer_expand(buf, sz + 1) == -1)
186 return (-1);
187
188 }
189 /* NOTREACHED */
190 }
191
192 //将数据格式化后添加到buf中
193 int
194 evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...)
195 {
196 int res = -1;
197 va_list ap;
198
199 va_start(ap, fmt);
200 res = evbuffer_add_vprintf(buf, fmt, ap);
201 va_end(ap);
202
203 return (res);
204 }
205
206 /* Reads data from an event buffer and drains the bytes read */
207 //从buf中读出datlen个字节存到data开始地址中
208 int
209 evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen)
210 {
211 size_t nread = datlen;
212 //最多只能读出缓冲区中所有数据
213 if (nread >= buf->off)
214 nread = buf->off;
215 //从buf->buffer地址的起始位置拷贝nread个字节到data开始的地址
216 memcpy(data, buf->buffer, nread);
217 //将nread个字节的数据排出缓冲区
218 evbuffer_drain(buf, nread);
219
220 return (nread);
221 }
222
223 /*
224 * Reads a line terminated by either '\r\n', '\n\r' or '\r' or '\n'.
225 * The returned buffer needs to be freed by the called.
226 */
227
228 //从缓冲区中读出一行
229 char *
230 evbuffer_readline(struct evbuffer *buffer)
231 {
232 //缓冲数据区的起始地址
233 u_char *data = EVBUFFER_DATA(buffer);
234 //缓冲区数据长度
235 size_t len = EVBUFFER_LENGTH(buffer);
236 char *line;
237 unsigned int i;
238
239 //读到\r或者\n
240 for (i = 0; i < len; i++) {
241 if (data[i] == '\r' || data[i] == '\n')
242 break;
243 }
244 //没读到回车或者换行,退出
245 if (i == len)
246 return (NULL);
247 //分配i+1字节内存,最后\0结尾
248 if ((line = malloc(i + 1)) == NULL) {
249 fprintf(stderr, "%s: out of memory\n", __func__);
250 return (NULL);
251 }
252 //从data起始地址开始复制i个字节到line中
253 memcpy(line, data, i);
254 line[i] = '\0';
255
256 /*
257 * Some protocols terminate a line with '\r\n', so check for
258 * that, too.
259 */
260 //如果i不是最后一个元素检查是否有\n或者\r。情况有可能有\r\n,\n\r,其中\r\r或者\n\n的情况排除,因为没用。
261 if ( i < len - 1 ) {
262 char fch = data[i], sch = data[i+1];
263
264 //情况有可能有\r\n,\n\r,其中\r\r或者\n\n的情况排除,因为没用
265 /* Drain one more character if needed */
266 if ( (sch == '\r' || sch == '\n') && sch != fch )
267 i += 1;
268 }
269
270 //将读取到的数据清除出缓冲区,i是序号从0开始,所以长度为i+1
271 evbuffer_drain(buffer, i + 1);
272
273 return (line);
274 }
275
276 //从缓冲区中读出一行,结束方式有4种
277 //EVBUFFER_EOL_ANY 任意数量的\r和\n
278 //EVBUFFER_EOL_CRLF \n或者\r\n
279 //EVBUFFER_EOL_CRLF_STRICT \r\n
280 //EVBUFFER_EOL_LF \n
281 char *
282 evbuffer_readln(struct evbuffer *buffer, size_t *n_read_out,
283 enum evbuffer_eol_style eol_style)
284 {
285 u_char *data = EVBUFFER_DATA(buffer);
286 u_char *start_of_eol, *end_of_eol;
287 size_t len = EVBUFFER_LENGTH(buffer);
288 char *line;
289 unsigned int i, n_to_copy, n_to_drain;
290
291 //如果n_read_out不为NULL,初始化为0
292 if (n_read_out)
293 *n_read_out = 0;
294
295 /* depending on eol_style, set start_of_eol to the first character
296 * in the newline, and end_of_eol to one after the last character. */
297 switch (eol_style) {
298 //任意数量的\r和\n
299 case EVBUFFER_EOL_ANY:
300 for (i = 0; i < len; i++) {
301 if (data[i] == '\r' || data[i] == '\n')
302 break;
303 }
304 if (i == len)
305 return (NULL);
306 //\r或者\n开始地址
307 start_of_eol = data+i;
308 ++i;
309 for ( ; i < len; i++) {
310 if (data[i] != '\r' && data[i] != '\n')
311 break;
312 }
313 //\r或者\n结束地址
314 end_of_eol = data+i;
315 break;
316 //\n或者\r\n
317 case EVBUFFER_EOL_CRLF:
318 //从data起始地址开始前len个字节查找\n字符
319 end_of_eol = memchr(data, '\n', len);
320 //没找到返回NULL
321 if (!end_of_eol)
322 return (NULL);
323 //前一个字符是\r
324 if (end_of_eol > data && *(end_of_eol-1) == '\r')
325 start_of_eol = end_of_eol - 1;
326 else
327 start_of_eol = end_of_eol;
328 //指向\n的下一个字节
329 end_of_eol++; /*point to one after the LF. */
330 break;
331 //\r\n
332 case EVBUFFER_EOL_CRLF_STRICT: {
333 u_char *cp = data;
334 //一直向前移动找到 "\r\n"的连续字符。
335 //如果\r后面不是\n,++cp,此时cp前面的数据就不用比较了
336 while ((cp = memchr(cp, '\r', len-(cp-data)))) {
337 if (cp < data+len-1 && *(cp+1) == '\n')
338 break;
339 if (++cp >= data+len) {
340 cp = NULL;
341 break;
342 }
343 }
344 if (!cp)
345 return (NULL);
346 start_of_eol = cp;
347 end_of_eol = cp+2;
348 break;
349 }
350 //\n
351 case EVBUFFER_EOL_LF:
352 start_of_eol = memchr(data, '\n', len);
353 if (!start_of_eol)
354 return (NULL);
355 end_of_eol = start_of_eol + 1;
356 break;
357 default:
358 return (NULL);
359 }
360 //数据区有多少个元素
361 n_to_copy = start_of_eol - data;
362 //数据缓冲区一共要排出的元素
363 n_to_drain = end_of_eol - data;
364
365 //n_to_copy+1带个结束字符 \0
366 if ((line = malloc(n_to_copy+1)) == NULL) {
367 event_warn("%s: out of memory\n", __func__);
368 return (NULL);
369 }
370 //数据复制到line中
371 memcpy(line, data, n_to_copy);
372 line[n_to_copy] = '\0';
373
374 //缓冲区清空读出的数据和末尾的结束符号
375 evbuffer_drain(buffer, n_to_drain);
376 //如果n_read_out不为NULL,返回读出的字符串字节数
377 if (n_read_out)
378 *n_read_out = (size_t)n_to_copy;
379
380 return (line);
381 }
382
383 /* Adds data to an event buffer */
384
385 //buf进行重新排列
386 static void
387 evbuffer_align(struct evbuffer *buf)
388 {
389 // 整个buffer
390 //| totallen |
391 //|--------------|-----------|-------------------------------------------|
392 //|misalign(偏移) |off(数据区) |datlen(需要加入的数据长度),大于totallen |
393 // | |
394 // \ /
395 // 整个buffer
396 //| totallen |
397 //|-----------|---------------------------------------------------------|
398 //|off(数据区) |datlen(需要加入的数据长度),大于totallen |
399
400
401 //缓冲区数据前移
402 //从buf->buffer拷贝off个字节到buf的orig_buffer
403 memmove(buf->orig_buffer, buf->buffer, buf->off);
404
405 //缓冲区数据起始位置变为buf起始位置
406 buf->buffer = buf->orig_buffer;
407
408 //偏移置为0
409 buf->misalign = 0;
410 }
411
412 /* Expands the available space in the event buffer to at least datlen */
413 //内存扩展
414 int
415 evbuffer_expand(struct evbuffer *buf, size_t datlen)
416 {
417 // 整个buffer
418 //| totallen |
419 //|--------------|-----------|-------------------------------------------|
420 //|misalign(偏移) |off(数据区) |datlen(需要加入的数据长度),大于totallen |
421 //
422
423 //首先判断是否需要扩展
424 size_t need = buf->misalign + buf->off + datlen;
425
426 //如果need小于totallen,无需扩展
427 /* If we can fit all the data, then we don't have to do anything */
428 if (buf->totallen >= need)
429 return (0);
430
431 /*
432 * If the misalignment fulfills our data needs, we just force an
433 * alignment to happen. Afterwards, we have enough space.
434 */
435 //如果偏移大于datlen,
436 if (buf->misalign >= datlen) {
437 //buf进行重新排列
438 evbuffer_align(buf);
439 } else {
440 //偏移小于datlen,数据元素大于totallen,需要重新分配内存
441 void *newbuf;
442 size_t length = buf->totallen;
443
444 //如果length小于256,length设置256
445 if (length < 256)
446 length = 256;
447 //如果length还是小于need,length扩大2倍直到不小于need
448 while (length < need)
449 length <<= 1;
450
451 //如果有偏移,先重新排列
452 if (buf->orig_buffer != buf->buffer)
453 evbuffer_align(buf);
454 //重新分配内存
455 if ((newbuf = realloc(buf->buffer, length)) == NULL)
456 return (-1);
457 //orig_buffer,buffer都赋值为新地址newbuf
458 buf->orig_buffer = buf->buffer = newbuf;
459 //总长度totallen为length
460 buf->totallen = length;
461 }
462
463 return (0);
464 }
465
466 //从data地址开始datlen个字节数据到evbuffer中
467 int
468 evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen)
469 {
470 // 整个buffer
471 //| totallen |
472 //|--------------|-----------|---------------------------|--------|
473 //|misalign(偏移) |off(数据区) |datlen(需要加入的数据长度) |剩余空间 |
474 //
475 size_t need = buf->misalign + buf->off + datlen;
476 size_t oldoff = buf->off;
477
478 //如果need大于了总长度,需要调整扩大
479 if (buf->totallen < need) {
480 //evbuffer调整扩大
481 if (evbuffer_expand(buf, datlen) == -1)
482 return (-1);
483 }
484 //将datlen长度的data数据复制到buffer中。
485 memcpy(buf->buffer + buf->off, data, datlen);
486 //复制成功,数据长度增加
487 buf->off += datlen;
488 //datlen不为0且buf有回调函数,调用回调函数,告知缓存变化
489 if (datlen && buf->cb != NULL)
490 (*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
491
492 return (0);
493 }
494
495 //drain:使流出;排掉水
496 //从缓冲区中读出len长度数据。
497 void
498 evbuffer_drain(struct evbuffer *buf, size_t len)
499 {
500 //记录当前缓冲区中的数据长度
501 size_t oldoff = buf->off;
502
503 //如果要读出的长度大于数据长度,就读出全部数据
504 if (len >= buf->off) {
505
506 // 整个buffer
507 //| totallen |
508 //|||-------------------------------------------------------------|
509 //|||剩余空间 |
510 //
511
512 //元素个数清零
513 buf->off = 0;
514 //数据缓冲地址前移到最前面的buf起始位置
515 buf->buffer = buf->orig_buffer;
516 //数据偏移置0
517 buf->misalign = 0;
518 goto done;
519 }
520 //如果读出数据不是全部数据
521
522 // 整个buffer
523 //| totallen |
524 //|--------------|-----------|------------------------------------|
525 //| misalign |off(数据区) | 剩余空间 |
526 // | |
527 // \ /
528
529 // 整个buffer
530 //| totallen |
531 //|-----------------|--------|------------------------------------|
532 //| misalign |off | 剩余空间 |
533 //
534
535 //buffer地址前移len
536 buf->buffer += len;
537 //misalign偏移加len
538 buf->misalign += len;
539 //由于读出数据,off减少len个数据
540 buf->off -= len;
541
542 done:
543 //缓冲区数据长度改变,调用回调函数
544 /* Tell someone about changes in this buffer */
545 if (buf->off != oldoff && buf->cb != NULL)
546 (*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
547
548 }
549
550 /*
551 * Reads data from a file descriptor into a buffer.
552 */
553
554 #define EVBUFFER_MAX_READ 4096
555 //从文件描述符中读取数据到buffer中
556 int
557 evbuffer_read(struct evbuffer *buf, int fd, int howmuch)
558 {
559 u_char *p;
560 size_t oldoff = buf->off;
561 int n = EVBUFFER_MAX_READ;
562
563 #if defined(FIONREAD)
564 #ifdef WIN32
565 long lng = n;
566 if (ioctlsocket(fd, FIONREAD, &lng) == -1 || (n=lng) <= 0) {
567 #else
568 if (ioctl(fd, FIONREAD, &n) == -1 || n <= 0) {
569 #endif
570 n = EVBUFFER_MAX_READ;
571 } else if (n > EVBUFFER_MAX_READ && n > howmuch) {
572 /*
573 * It's possible that a lot of data is available for
574 * reading. We do not want to exhaust resources
575 * before the reader has a chance to do something
576 * about it. If the reader does not tell us how much
577 * data we should read, we artifically limit it.
578 */
579 if ((size_t)n > buf->totallen << 2)
580 n = buf->totallen << 2;
581 if (n < EVBUFFER_MAX_READ)
582 n = EVBUFFER_MAX_READ;
583 }
584 #endif
585 //buffer最多读EVBUFFER_MAX_READ个字节
586 if (howmuch < 0 || howmuch > n)
587 howmuch = n;
588
589 /* If we don't have FIONREAD, we might waste some space here */
590 //如果需要读的howmuch个字节数据,首先扩展buffer。因为我们没有FIONREAD参数,有可能howmuch很大,所以可能
591 //会浪费内存
592 if (evbuffer_expand(buf, howmuch) == -1)
593 return (-1);
594
595 /* We can append new data at this point */
596 //读入数据的起始位置
597 p = buf->buffer + buf->off;
598
599 //读数据
600 #ifndef WIN32
601 n = read(fd, p, howmuch);
602 #else
603 n = recv(fd, p, howmuch, 0);
604 #endif
605 if (n == -1)
606 return (-1);
607 if (n == 0)
608 return (0);
609
610 //缓冲区数据长度+n
611 buf->off += n;
612
613 //缓冲区数据改变,有回调,调用回调
614 /* Tell someone about changes in this buffer */
615 if (buf->off != oldoff && buf->cb != NULL)
616 (*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
617
618 return (n);
619 }
620
621 //将缓冲区数据读出,写入到fd文件描述符对应的文件中
622 int
623 evbuffer_write(struct evbuffer *buffer, int fd)
624 {
625 int n;
626 //从buffer开始,将off个字节写入fd
627 #ifndef WIN32
628 n = write(fd, buffer->buffer, buffer->off);
629 #else
630 n = send(fd, buffer->buffer, buffer->off, 0);
631 #endif
632 //发生错误
633 if (n == -1)
634 return (-1);
635 //关闭写
636 if (n == 0)
637 return (0);
638 //写入fd成功,将缓冲区中排出已经写入的n个字节
639 evbuffer_drain(buffer, n);
640
641 return (n);
642 }
643 //从buffer中查找从what地址开始的长度为len的字符串
644 u_char *
645 evbuffer_find(struct evbuffer *buffer, const u_char *what, size_t len)
646 {
647 u_char *search = buffer->buffer, *end = search + buffer->off;
648 u_char *p;
649 //从search所指内存区域的前end - search个字节查找字符*what(首字符)
650 while (search < end &&
651 (p = memchr(search, *what, end - search)) != NULL) {
652 //当前位置p+len大于end,已经不可能找到从what地址开始的长度为len的字符串,跳出
653 if (p + len > end)
654 break;
655 //比较p开始的内存和what开始的内存区域的前len个字节
656 if (memcmp(p, what, len) == 0)
657 return (p);
658 //p开始的内存和what开始的内存区域的前len个字节不匹配,地址p+1,继续查找
659 search = p + 1;
660 }
661
662 return (NULL);
663 }
664
665 //设置回调函数和回调参数
666 void evbuffer_setcb(struct evbuffer *buffer,
667 void (*cb)(struct evbuffer *, size_t, size_t, void *),
668 void *cbarg)
669 {
670 //设置回调函数
671 buffer->cb = cb;
672
673 //设置回调参数
674 buffer->cbarg = cbarg;
675 }

libevent中数据缓冲区buffer分析的更多相关文章

  1. Java NIO中的缓冲区Buffer(一)缓冲区基础

    什么是缓冲区(Buffer) 定义 简单地说就是一块存储区域,哈哈哈,可能太简单了,或者可以换种说法,从代码的角度来讲(可以查看JDK中Buffer.ByteBuffer.DoubleBuffer等的 ...

  2. Java基础(三十四)String、StringBuffer类和数据缓冲区Buffer类

    一.String类 1.创建字符串对象 创建字符串对象有两种方法:直接用“=”或者使用“new String(...)” String aStr = "TMZ"; String b ...

  3. unix中数据缓冲区高速缓冲的设计

    目录 1. 概述 2. 缓冲区的设计 2.1 缓冲区头部 2.2 缓冲区的结构 2.3 缓冲区的检索算法 2.3. 申请一个缓冲区算法 getblk 2.3.2 释放一个缓冲区算法 brelse 2. ...

  4. Java NIO中的缓冲区Buffer(二)创建/复制缓冲区

    创建缓冲区的方式 主要有以下两种方式创建缓冲区: 1.调用allocate方法 2.调用wrap方法 我们将以charBuffer为例,阐述各个方法的含义: allocate方法创建缓冲区 调用all ...

  5. Java-NIO(二):缓冲区(Buffer)的数据存取

    缓冲区(Buffer): 一个用于特定基本数据类行的容器.有java.nio包定义的,所有缓冲区都是抽象类Buffer的子类. Java NIO中的Buffer主要用于与NIO通道进行交互,数据是从通 ...

  6. NIO之缓冲区(Buffer)的数据存取

    缓冲区(Buffer) 一个用于特定基本数据类行的容器.有java.nio包定义的,所有缓冲区都是抽象类Buffer的子类. Java NIO中的Buffer主要用于与NIO通道进行交互,数据是从通道 ...

  7. Java NIO -- 缓冲区(Buffer)的数据存取

    缓冲区(Buffer): 一个用于特定基本数据类型的容器.由 java.nio 包定义的,所有缓冲区都是 Buffer 抽象类的子类.Java NIO 中的 Buffer 主要用于与 NIO 通道进行 ...

  8. 一个I/O线程可以并发处理N个客户端连接和读写操作 I/O复用模型 基于Buf操作NIO可以读取任意位置的数据 Channel中读取数据到Buffer中或将数据 Buffer 中写入到 Channel 事件驱动消息通知观察者模式

    Tomcat那些事儿 https://mp.weixin.qq.com/s?__biz=MzI3MTEwODc5Ng==&mid=2650860016&idx=2&sn=549 ...

  9. 缓冲区(Buffer)的数据存取

    缓冲区(Buffer) 1. 缓冲区(Buffer):一个用于特定基本数据类 型的容器. 由 java.nio 包定义的,所有缓冲区 都是 Buffer 抽象类的子类.2. Java NIO 中的 B ...

随机推荐

  1. Css预编语言以及区别

    一.是什么 Css 作为一门标记性语言,语法相对简单,对使用者的要求较低,但同时也带来一些问题 需要书写大量看似没有逻辑的代码,不方便维护及扩展,不利于复用,尤其对于非前端开发工程师来讲,往往会因为缺 ...

  2. Fiddler高级用法

    Fiddler高级用法 1. 简单用法 Fiddler作为一个基于http协议的抓包工具,一直在业界有广泛使用.很多测试或者前端在使用Fiddler时,仅仅用于查看前端和服务端之间的请求信息.包括我作 ...

  3. 1057 Stack

    Stack is one of the most fundamental data structures, which is based on the principle of Last In Fir ...

  4. 使用 mvn install 命令将本地jar包注册到本地maven仓库

    前提: 要安装maven并配置环境变量. windows 系统环境变量配置 新建环境变量:MAVEN_HOME    值为:maven的解压包路径或安装路径. 在path 环境变量中添加:%MAVEN ...

  5. 【Vue】Vue学习(一)-Vue指令

    1.v-text v-text主要用来更新文本,等同于JS的text属性 <span v-text="msg"></span> 这两者等价 <span ...

  6. hdu4990 矩阵快速幂

    题意:       给你一短代码,让你优化这个代码,代码如下 #pragma comment(linker, "/STACK:1024000000,1024000000") #in ...

  7. 设计模式-UML图简单介绍

    直接上法宝: 1.类(Class)     类图分三层:     第一层显示类的名称,如果是抽象类,则就用斜体显示.     第二层是类的特性,通常就是字段和属性.     第三层是类的操作,通常是方 ...

  8. UVA11427玩纸牌(全概率+递推)

    题意:       一个人玩纸牌游戏,他每天最多玩n局,枚举获胜的概率是a/b,每天玩牌只要获胜概率达到p,那么他今天就不玩了,明天接着玩,如果有一天他的概率没有达到p,(没有达到p的话他今天一定是玩 ...

  9. IOS Widget(4-1):创建可配置小组件(静态配置数据)

    引言   经过前面几篇文章阅读,已经掌握开发一款小组件的基本技能了,接下来开始掌握一些相对高级一点的技能.本文创建一个可配置小组件,通过修改时间类型,让Text空间显示不同格式的时间. 本文大纲 添加 ...

  10. 音视频开发:为什么推荐使用Jetpack CameraX?

    我们的生活已经越来越离不开相机,从自拍到直播,扫码再到VR等等.相机的优劣自然就成为了厂商竞相追逐的赛场.对于app开发者来说,如何快速驱动相机,提供优秀的拍摄体验,优化相机的使用功耗,是一直以来追求 ...