(转)Libevent(4)— Bufferevent

转自:http://name5566.com/4215.html

参考文献列表:
http://www.wangafu.net/~nickm/libevent-book/

此文编写的时候,使用到的 Libevent 为 2.0.21

Buffer IO 模式

bufferevent 提供给我们一种 Buffer IO 模式(这里以写入数据为例):

  1. 在我们需要通过某个连接发送数据的时候,先将等待发送的数据放入到一个 buffer 中
  2. 等待此连接可以写入数据
  3. 尽可能多的获取 buffer 中的数据写入此连接
  4. 如果 buffer 中还有需要写入的数据则继续等待直到此连接可以写入数据

每一个 bufferevent 都包含了一个输入 buffer 和一个输出 buffer,它们的类型为 evbuffer(结构体)。当我们向 bufferevent 写入数据的时候,实际上数据首先被写入到了输出 buffer,当 bufferevent 有数据可读时,我们实际上是从输入 buffer 中获取数据。

目前 bufferevent 目前仅仅支持 stream-oriented 的协议(例如 TCP)并不支持 datagram-oriented 协议(例如 UDP)。一个 bufferevent 的实例负责一个特定的连接上的数据收发。

Libevent 可以按需要创建多种类型的 bufferevent:

  1. 基于 socket 的 bufferevent。此类型的 bufferevent 使用 socket 来进行数据的收发,使用 event 机制来判断 socket 是否可以进行读写操作
  2. 异步 IO bufferevent。此类型的 bufferevent 使用 IOCP 接口实现(仅 Windows 下可用且目前处于实验阶段)
  3. Filtering bufferevent。此类型的 bufferevent 可以在同底层交互时完成一些额外的数据处理工作,例如可以完成数据的压缩和解析工作。这种类型的 bufferevent 的一个实例会封装了另外的一个 bufferevent,我们把这个被封装的 bufferevent 叫做底层 bufferevent
  4. Paired bufferevent。本文不谈

bufferevent 的回调函数

每个 bufferevent 实例可以有 3 个回调函数(通过接口 bufferevent_setcb 设置):

  1. 读取回调函数。默认情况下,只要从底层读取到了数据此回调函数将被调用
  2. 写入回调函数。默认情况下,足够多的数据被写入底层此回调函数将被调用
  3. 事件回调函数。当某些事件(错误)发生时被调用

对于 buffer 的读、写和回调行为可以通过几个参数来配置,这几个参数在 Libevent 中被叫做 watermark(水位标记,我们可以将 buffer 想象为一个水池,水位标记用于标记水池中水的多少,也就是说,watermark 用于标记 buffer 中的数据量)。watermark 被实现为整数(类型为 size_t),有几种类型的 watermark:

  1. Read low-water mark 用于控制读取回调函数的行为。当 bufferevent 进行读取操作时,Read low-water mark 的值决定了输入 buffer 有多少数据后调用读取回调函数。默认的情况下,此值为 0,因此 bufferevent 读取操作都会导致读取回调函数被调用
  2. Read high-water mark 用于控制输入 buffer 的大小。如果输入 buffer 中的数据量达到 Read high-water mark 的值,那么 bufferevent 将停止读取。默认的情况下,此值为无限大
  3. Write low-water mark,用于控制写入回调函数的行为。当 bufferevent 进行写入操作时,Write low-water mark 的值决定了输出 buffer 有多少数据后调用写入回调函数。默认的情况下,此值为 0,因此写入回调函数会在输出 buffer 为空的时候被调用
  4. Write high-water mark,此值在使用 Filtering bufferevent 有特殊的用途

在一些特殊需求中(详细并不讨论),我们可能需要回调函数被延时执行(这种被延时的回调函数被叫做 Deferred callbacks)。延时回调函数会在事件循环中排队,并在普通事件回调函数(regular event’s callback)之后被调用。

从基于 socket 的 bufferevent 开始认识 bufferevent

创建基于 socket 的 bufferevent:

  1. // 创建一个基于 socket 的 bufferevent
  2. // 函数执行失败返回 NULL
  3. struct bufferevent *bufferevent_socket_new(
  4. struct event_base *base,
  5. // socket 文件描述符
  6. // 此 socket 必须被设置为非阻塞的
  7. // 可以设置为 -1 表示之后再设置
  8. evutil_socket_t fd,
  9. // bufferevent 的选项
  10. enum bufferevent_options options);

buffervent 的选项可以用来改变 bufferevent 的行为。可用的选项包括:

  1. BEV_OPT_CLOSE_ON_FREE
    当 bufferevent 被释放同时关闭底层(socket 被关闭等)
  2. BEV_OPT_THREADSAFE
    为 bufferevent 自动分配锁,这样能够在多线程环境中安全使用
  3. BEV_OPT_DEFER_CALLBACKS
    当设置了此标志,bufferevent 会延迟它的所有回调(参考前面说的延时回调)
  4. BEV_OPT_UNLOCK_CALLBACKS
    如果 bufferevent 被设置为线程安全的,用户提供的回调被调用时 bufferevent 的锁会被持有
    如果设置了此选项,Libevent 将在调用你的回调时释放 bufferevent 的锁

建立连接:

  1. // address 和 addrlen 和标准的 connect 函数的参数没有区别
  2. // 如果 bufferevent bev 没有设置 socket(在创建时可以设置 socket)
  3. // 那么调用此函数将分配一个新的 socket 给 bev
  4. // 连接成功返回 0 失败返回 -1
  5. int bufferevent_socket_connect(struct bufferevent *bev,
  6. struct sockaddr *address, int addrlen);

简单的一个范例:

  1. #include <event2/event.h>
  2. #include <event2/bufferevent.h>
  3. #include <sys/socket.h>
  4. #include <string.h>
  5. // 事件回调函数
  6. void eventcb(struct bufferevent *bev, short events, void *ptr)
  7. {
  8. // 连接成功建立
  9. if (events & BEV_EVENT_CONNECTED) {
  10. /* We‘re connected to 127.0.0.1:8080. Ordinarily we‘d do
  11. something here, like start reading or writing. */
  12. // 出现错误
  13. } else if (events & BEV_EVENT_ERROR) {
  14. /* An error occured while connecting. */
  15. }
  16. }
  17. int main_loop(void)
  18. {
  19. struct event_base *base;
  20. struct bufferevent *bev;
  21. struct sockaddr_in sin;
  22. base = event_base_new();
  23. // 初始化连接地址
  24. memset(&sin, 0, sizeof(sin));
  25. sin.sin_family = AF_INET;
  26. sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */
  27. sin.sin_port = htons(8080); /* Port 8080 */
  28. // 创建一个基于 socket 的 bufferevent
  29. // 参数 -1 表示并不为此 bufferevent 设置 socket
  30. bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
  31. // 为 bufferevent bev 设置回调函数
  32. // 这里仅仅设置了事件回调函数
  33. // 后面会详细谈及此函数
  34. bufferevent_setcb(bev, NULL, NULL, eventcb, NULL);
  35. // 进行连接
  36. if (bufferevent_socket_connect(bev,
  37. (struct sockaddr *)&sin, sizeof(sin)) < 0) {
  38. /* Error starting connection */
  39. bufferevent_free(bev);
  40. return -1;
  41. }
  42. // 开始事件循环
  43. event_base_dispatch(base);
  44. return 0;
  45. }

更多的 bufferevent API

释放 bufferevent

  1. // 如果存在未完成的延时回调,bufferevent 会在回调完成后才被真正释放
  2. void bufferevent_free(struct bufferevent *bev);

bufferevent 回调函数的设置和获取

  1. // 读取、写入回调函数原型
  2. // ctx 为用户自定义数据(由 bufferevent_setcb 设定)
  3. typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx);
  4. // 事件回调函数原型
  5. // ctx 为用户自定义数据(由 bufferevent_setcb 设定)
  6. // events 选项可以为:
  7. // BEV_EVENT_READING --- 在 bufferevent 上进行读取操作时出现了一个事件
  8. // BEV_EVENT_WRITING --- 在 bufferevent 上进行写入操作时出现了一个事件
  9. // BEV_EVENT_ERROR --- 进行 bufferevent 操作时(例如调用 bufferevent API)出错,获取详细的错误信息使用 EVUTIL_SOCKET_ERROR()
  10. // BEV_EVENT_TIMEOUT --- 在 bufferevent 上出现了超时
  11. // BEV_EVENT_EOF --- 在 bufferevent 上遇到了文件结束符
  12. // BEV_EVENT_CONNECTED --- 在 bufferevent 上请求连接完成了
  13. typedef void (*bufferevent_event_cb)(struct bufferevent *bev, short events, void *ctx);
  14. // 设置回调函数
  15. // 如果希望禁用回调函数,那么设置对应的参数为 NULL
  16. void bufferevent_setcb(
  17. // bufferevent
  18. struct bufferevent *bufev,
  19. // 读取回调函数
  20. bufferevent_data_cb readcb,
  21. // 写入回调函数
  22. bufferevent_data_cb writecb,
  23. // 事件回调函数
  24. bufferevent_event_cb eventcb,
  25. // 用户定义的数据
  26. // 这三个回调函数均共享此参数
  27. void *cbarg
  28. );
  29. // 取回回调函数
  30. // 参数为 NULL 表示忽略
  31. void bufferevent_getcb(
  32. struct bufferevent *bufev,
  33. bufferevent_data_cb *readcb_ptr,
  34. bufferevent_data_cb *writecb_ptr,
  35. bufferevent_event_cb *eventcb_ptr,
  36. void **cbarg_ptr
  37. );

设置 watermark

  1. // events 参数可以为
  2. // EV_READ 表示设置 read watermark
  3. // EV_WRITE 表示设置 write watermark
  4. // EV_READ | EV_WRITE 表示设置 read 以及 write watermark
  5. void bufferevent_setwatermark(struct bufferevent *bufev, short events,
  6. size_t lowmark, size_t highmark);

获取到输入和输出 buffer

  1. // 获取到输入 buffer
  2. struct evbuffer *bufferevent_get_input(struct bufferevent *bufev);
  3. // 获取到输出 buffer
  4. struct evbuffer *bufferevent_get_output(struct bufferevent *bufev);

添加数据到输出 buffer

  1. // 下面两个函数执行成功返回 0 失败返回 -1
  2. // 向 bufev 的输出 buffer 中添加大小为 size 的数据
  3. // 数据的首地址为 data
  4. int bufferevent_write(struct bufferevent *bufev,
  5. const void *data, size_t size);
  6. // 向 bufev 的输出 buffer 中添加数据
  7. // 数据来源于 buf
  8. // 此函数会清除 buf 中的所有数据
  9. int bufferevent_write_buffer(struct bufferevent *bufev,
  10. struct evbuffer *buf);

从输入 buffer 中获取数据

  1. // 从 bufev 的输入 buffer 中获取最多 size 字节的数据保存在 data 指向的内存中
  2. // 此函数返回实际读取的字节数
  3. size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
  4. // 获取 bufev 的输入 buffer 中的所有数据并保存在 buf 中
  5. // 此函数成功返回 0 失败返回 -1
  6. int bufferevent_read_buffer(struct bufferevent *bufev,
  7. struct evbuffer *buf);

关于 bufferevent 的一些高级话题,可以参考:http://www.wangafu.net/~nickm/libevent-book/Ref6a_advanced_bufferevents.html

evbuffers

evbuffer 是一个队列,在其尾部添加数据和在其头部删除数据均被优化了。evbuffer 相关的 API 在这里可以查看:http://www.wangafu.net/~nickm/libevent-book/Ref7_evbuffer.html

时间: 2024-08-12 14:53:47

(转)Libevent(4)— Bufferevent的相关文章

libevent(九)bufferevent

接上文: libevent(八)bufferevent 在用户的回调函数中,通过bufferevent_read从输入缓冲input中读数据,相应地,通过bufferevent_write向输出缓冲output中写数据. 关于写事件,这里要多说几句. 对于一个fd,只要它的写缓冲区没有满,就会触发写事件.一般情况下,如果不向这个fd发送大量的数据,它的写缓冲区是不会满的. 所以如果一开始就监听写事件,那么写事件会一直被触发. libevent的做法是:当我们确实要写入数据时,才监听写事件. 下面

libevent(十)bufferevent 2

接上文libevent(九)bufferevent 上文主要讲了bufferevent如何监听读事件,那么bufferevent如何监听写事件呢? 对于一个fd,只要它的写缓冲区没有满,就会触发写事件. 一般情况下,如果不向这个fd发送大量的数据,它的写缓冲区是不会满的. 所以,如果一开始就监听写事件,写事件会一直被触发. libevent的做法是: 当我们确实要向fd写入数据时,才监听该fd的写事件. 监听写事件 在用户回调函数中,可以通过 bufferevent_write 向输出缓冲out

libevent+bufferevent总结

libevent+bufferevent总结 1 学习参考网址 libevent学习网址:http://blog.csdn.net/feitianxuxue/article/details/9372535 http://www.cnblogs.com/hustcat/archive/2010/08/31/1814022.html http://www.cppblog.com/mysileng/archive/2013/02/04/197719.html bufferevent学习网址:http:

libevent粘包分包解决方案:bufferevent + evbuffer

转自:http://blog.sina.com.cn/s/blog_9f1496990102vshz.html 原文:http://www.lvtao.net/c/631.html Libevent介绍 libevent是一个事件触发的网络库,适用于windows.linux.bsd等多种平台,内部使用select.epoll.kqueue等系统调用管理事件机制.著名分布式缓存软件memcached也是libevent based,而且libevent在使用上可以做到跨平台,而且根据libeve

libevent学习七

Bufferevents:概念和基础 很多时候,一个程序需要处理一些数据的缓存,不止应用在答复event上.例如:当我们需要去写出数据,通常会这样做: 1. 发现有数据需要写出到一条连接上:把这些数据放到buffer里. 2. 等连接变成可写的状态. 3. 尽可能的写入数据. 4. 记住我们写了多少数据,然后如果数据没有全部写完,就等连接再次变为可写的状态. 这种IO缓冲模式已经足够Libevent的日常使用.一个"bufferevent"是由一个底层传输渠道(如socket),一个读

项目中的Libevent(多线程)

多线程版Libevent //保存线程的结构体 struct LibeventThread { LibEvtServer* that; //用作传参 std::shared_ptr<std::thread> spThread; // 线程 struct event_base * thread_base; // 事件根基 struct event notify_event; evutil_socket_t notfiy_recv_fd; // socketpair 接收端fd(工作线程接收通知)

libevent网络编程汇总

libevent源码剖析: ========================================================== 1.libevent源码剖析一(序) 2.libevent源码剖析二(Reactor框架) 3.libevent源码剖析三(基础使用) 4.libevent源码剖析四(代码组织) 5.libevent源码剖析五(核心:event) 6.libevent源码剖析六(事件处理:event_base) 7.libevent源码剖析七(事件主循环) 8.lib

libevent(1)

很多时候,除了响应事件之外,应用还希望做一定的数据缓冲.比如说,写入数据的时候,通常的运行模式是: l 决定要向连接写入一些数据,把数据放入到缓冲区中 l 等待连接可以写入 l 写入尽量多的数据 l 记住写入了多少数据,如果还有更多数据要写入,等待连接再次可以写入 这种缓冲IO模式很通用,libevent为此提供了一种通用机制,即bufferevent.bufferevent由一个底层的传输端口(如套接字),一个读取缓冲区和一个写入缓冲区组成.与通常的事件在底层传输端口已经就绪,可以读取或者写入

reactor模型框架图和流程图 libevent

学习libevent有助于提升程序设计功力,除了网络程序设计方面外,libevent的代码里有很多有用的设计技巧和基础数据结构,比如信息隐藏.函数指针.c语言的多态支持.链表和堆等等,都有助于提升自身的程序功力.       程序设计不止要了解框架,很多细节之处恰恰也是事关整个系统成败的关键.只对libevent本身的框架大概了解,那或许仅仅是一知半解,不深入代码分析,就难以了解其设计的精巧之处,也就难以为自己所用.       事实上libevent本身就是一个典型的Reactor模型,理解R

libevent学习七(bufferevent)

1. 每个bufferevent 都拥有类型为struct evbuffer的input buffer和out buffer,分别供数据读取和数据写入使用. 2.读取和写入数据是通过编写和设置对应的回调函数进行,而调用回调函数的时机则根据水位是否满足来的,水位又是可以设置的.默认情况下读的低水位是0,就是说libevent从底层读到大于0的数据到input buffer中,读回调函数就会调用,读回调函数读取input buffer的数据:同样默认的写水位也为0,就是说一旦output buffe