很多时候为了应对数据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分析的更多相关文章
-
Java NIO中的缓冲区Buffer(一)缓冲区基础
什么是缓冲区(Buffer) 定义 简单地说就是一块存储区域,哈哈哈,可能太简单了,或者可以换种说法,从代码的角度来讲(可以查看JDK中Buffer.ByteBuffer.DoubleBuffer等的 ...
-
Java基础(三十四)String、StringBuffer类和数据缓冲区Buffer类
一.String类 1.创建字符串对象 创建字符串对象有两种方法:直接用“=”或者使用“new String(...)” String aStr = "TMZ"; String b ...
-
unix中数据缓冲区高速缓冲的设计
目录 1. 概述 2. 缓冲区的设计 2.1 缓冲区头部 2.2 缓冲区的结构 2.3 缓冲区的检索算法 2.3. 申请一个缓冲区算法 getblk 2.3.2 释放一个缓冲区算法 brelse 2. ...
-
Java NIO中的缓冲区Buffer(二)创建/复制缓冲区
创建缓冲区的方式 主要有以下两种方式创建缓冲区: 1.调用allocate方法 2.调用wrap方法 我们将以charBuffer为例,阐述各个方法的含义: allocate方法创建缓冲区 调用all ...
-
Java-NIO(二):缓冲区(Buffer)的数据存取
缓冲区(Buffer): 一个用于特定基本数据类行的容器.有java.nio包定义的,所有缓冲区都是抽象类Buffer的子类. Java NIO中的Buffer主要用于与NIO通道进行交互,数据是从通 ...
-
NIO之缓冲区(Buffer)的数据存取
缓冲区(Buffer) 一个用于特定基本数据类行的容器.有java.nio包定义的,所有缓冲区都是抽象类Buffer的子类. Java NIO中的Buffer主要用于与NIO通道进行交互,数据是从通道 ...
-
Java NIO -- 缓冲区(Buffer)的数据存取
缓冲区(Buffer): 一个用于特定基本数据类型的容器.由 java.nio 包定义的,所有缓冲区都是 Buffer 抽象类的子类.Java NIO 中的 Buffer 主要用于与 NIO 通道进行 ...
-
一个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 ...
-
缓冲区(Buffer)的数据存取
缓冲区(Buffer) 1. 缓冲区(Buffer):一个用于特定基本数据类 型的容器. 由 java.nio 包定义的,所有缓冲区 都是 Buffer 抽象类的子类.2. Java NIO 中的 B ...
随机推荐
-
各国货币M2增长对比
自2006至2016年的10年间, 卢布M2增长了3.8倍, 人民币M2增长了3.4倍, 美元M2增长1倍, 欧元/英镑M2增长0.8倍, 日元M2增长0.5倍
-
BZOJ2322: [BeiJing2011]梦想封印
Description 渐渐地,Magic Land上的人们对那座岛屿上的各种现象有了深入的了解. 为了分析一种奇特的称为梦想封印(Fantasy Seal)的特技,需要引入如下的概念: 每一位魔法的 ...
-
poj 2509 Peter&#39;s smokes
http://poj.org/problem?id=2509 Peter's smokes Time Limit: 1000MS Memory Limit: 65536K Total Submis ...
-
Camel、Pastal、匈牙利标记法区别及联系
在英语中,依靠单词的大小写拼写复合词的做法,叫做"骆驼拼写法"(CamelCase).比如,backColor这个复合词,color的第一个字母采用大写. 这种拼写法在正规的英语中 ...
-
【转】tkinter实现的文本编辑器
此代码是看完如下教学视频实现的,所以算是[转载]吧: 效果: 代码: # -*- encodin ...
-
ZigBee研究之旅(一)
*********************************************************************** 以下有引用webee公司的文档的内容,版权属于webee ...
-
ScalaPB(2): 在scala中用gRPC实现微服务
gRPC是google开源提供的一个RPC软件框架,它的特点是极大简化了传统RPC的开发流程和代码量,使用户可以免除许多陷阱并聚焦于实际应用逻辑中.作为一种google的最新RPC解决方案,gRPC具 ...
-
P4630 [APIO2018] Duathlon 铁人两项
思路 圆方树,一个点双中的所有点都可以被经过,所以给圆点赋值-1,方点赋值为圆点个数,统计圆点两两之间的路径权值和即可 代码 #include <cstdio> #include < ...
-
【Java每日一题】20170223
20170222问题解析请点击今日问题下方的“[Java每日一题]20170223”查看(问题解析在公众号首发,公众号ID:weknow619) package Feb2017; public cla ...
-
什么是Docker?(一)
Docker 最初是 dotCloud 公司创始人 Solomon Hykes 在法国期间发起的一个公司内部项目,它是基于 dotCloud 公司多年云服务技术的一次革新,并于 2013 年 3 月以 ...