(转)Libevent(4)— Bufferevent
转自:http://name5566.com/4215.html
参考文献列表:
http://www.wangafu.net/~nickm/libevent-book/
此文编写的时候,使用到的 Libevent 为 2.0.21
Buffer IO 模式
bufferevent 提供给我们一种 Buffer IO 模式(这里以写入数据为例):
- 在我们需要通过某个连接发送数据的时候,先将等待发送的数据放入到一个 buffer 中
- 等待此连接可以写入数据
- 尽可能多的获取 buffer 中的数据写入此连接
- 如果 buffer 中还有需要写入的数据则继续等待直到此连接可以写入数据
每一个 bufferevent 都包含了一个输入 buffer 和一个输出 buffer,它们的类型为 evbuffer(结构体)。当我们向 bufferevent 写入数据的时候,实际上数据首先被写入到了输出 buffer,当 bufferevent 有数据可读时,我们实际上是从输入 buffer 中获取数据。
目前 bufferevent 目前仅仅支持 stream-oriented 的协议(例如 TCP)并不支持 datagram-oriented 协议(例如 UDP)。一个 bufferevent 的实例负责一个特定的连接上的数据收发。
Libevent 可以按需要创建多种类型的 bufferevent:
- 基于 socket 的 bufferevent。此类型的 bufferevent 使用 socket 来进行数据的收发,使用 event 机制来判断 socket 是否可以进行读写操作
- 异步 IO bufferevent。此类型的 bufferevent 使用 IOCP 接口实现(仅 Windows 下可用且目前处于实验阶段)
- Filtering bufferevent。此类型的 bufferevent 可以在同底层交互时完成一些额外的数据处理工作,例如可以完成数据的压缩和解析工作。这种类型的 bufferevent 的一个实例会封装了另外的一个 bufferevent,我们把这个被封装的 bufferevent 叫做底层 bufferevent
- Paired bufferevent。本文不谈
bufferevent 的回调函数
每个 bufferevent 实例可以有 3 个回调函数(通过接口 bufferevent_setcb 设置):
- 读取回调函数。默认情况下,只要从底层读取到了数据此回调函数将被调用
- 写入回调函数。默认情况下,足够多的数据被写入底层此回调函数将被调用
- 事件回调函数。当某些事件(错误)发生时被调用
对于 buffer 的读、写和回调行为可以通过几个参数来配置,这几个参数在 Libevent 中被叫做 watermark(水位标记,我们可以将 buffer 想象为一个水池,水位标记用于标记水池中水的多少,也就是说,watermark 用于标记 buffer 中的数据量)。watermark 被实现为整数(类型为 size_t),有几种类型的 watermark:
- Read low-water mark 用于控制读取回调函数的行为。当 bufferevent 进行读取操作时,Read low-water mark 的值决定了输入 buffer 有多少数据后调用读取回调函数。默认的情况下,此值为 0,因此 bufferevent 读取操作都会导致读取回调函数被调用
- Read high-water mark 用于控制输入 buffer 的大小。如果输入 buffer 中的数据量达到 Read high-water mark 的值,那么 bufferevent 将停止读取。默认的情况下,此值为无限大
- Write low-water mark,用于控制写入回调函数的行为。当 bufferevent 进行写入操作时,Write low-water mark 的值决定了输出 buffer 有多少数据后调用写入回调函数。默认的情况下,此值为 0,因此写入回调函数会在输出 buffer 为空的时候被调用
- Write high-water mark,此值在使用 Filtering bufferevent 有特殊的用途
在一些特殊需求中(详细并不讨论),我们可能需要回调函数被延时执行(这种被延时的回调函数被叫做 Deferred callbacks)。延时回调函数会在事件循环中排队,并在普通事件回调函数(regular event’s callback)之后被调用。
从基于 socket 的 bufferevent 开始认识 bufferevent
创建基于 socket 的 bufferevent:
- // 创建一个基于 socket 的 bufferevent
- // 函数执行失败返回 NULL
- struct bufferevent *bufferevent_socket_new(
- struct event_base *base,
- // socket 文件描述符
- // 此 socket 必须被设置为非阻塞的
- // 可以设置为 -1 表示之后再设置
- evutil_socket_t fd,
- // bufferevent 的选项
- enum bufferevent_options options);
buffervent 的选项可以用来改变 bufferevent 的行为。可用的选项包括:
- BEV_OPT_CLOSE_ON_FREE
当 bufferevent 被释放同时关闭底层(socket 被关闭等) - BEV_OPT_THREADSAFE
为 bufferevent 自动分配锁,这样能够在多线程环境中安全使用 - BEV_OPT_DEFER_CALLBACKS
当设置了此标志,bufferevent 会延迟它的所有回调(参考前面说的延时回调) - BEV_OPT_UNLOCK_CALLBACKS
如果 bufferevent 被设置为线程安全的,用户提供的回调被调用时 bufferevent 的锁会被持有
如果设置了此选项,Libevent 将在调用你的回调时释放 bufferevent 的锁
建立连接:
- // address 和 addrlen 和标准的 connect 函数的参数没有区别
- // 如果 bufferevent bev 没有设置 socket(在创建时可以设置 socket)
- // 那么调用此函数将分配一个新的 socket 给 bev
- // 连接成功返回 0 失败返回 -1
- int bufferevent_socket_connect(struct bufferevent *bev,
- struct sockaddr *address, int addrlen);
简单的一个范例:
- #include <event2/event.h>
- #include <event2/bufferevent.h>
- #include <sys/socket.h>
- #include <string.h>
- // 事件回调函数
- void eventcb(struct bufferevent *bev, short events, void *ptr)
- {
- // 连接成功建立
- if (events & BEV_EVENT_CONNECTED) {
- /* We're connected to 127.0.0.1:8080. Ordinarily we'd do
- something here, like start reading or writing. */
- // 出现错误
- } else if (events & BEV_EVENT_ERROR) {
- /* An error occured while connecting. */
- }
- }
- int main_loop(void)
- {
- struct event_base *base;
- struct bufferevent *bev;
- struct sockaddr_in sin;
- base = event_base_new();
- // 初始化连接地址
- memset(&sin, 0, sizeof(sin));
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */
- sin.sin_port = htons(8080); /* Port 8080 */
- // 创建一个基于 socket 的 bufferevent
- // 参数 -1 表示并不为此 bufferevent 设置 socket
- bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
- // 为 bufferevent bev 设置回调函数
- // 这里仅仅设置了事件回调函数
- // 后面会详细谈及此函数
- bufferevent_setcb(bev, NULL, NULL, eventcb, NULL);
- // 进行连接
- if (bufferevent_socket_connect(bev,
- (struct sockaddr *)&sin, sizeof(sin)) < 0) {
- /* Error starting connection */
- bufferevent_free(bev);
- return -1;
- }
- // 开始事件循环
- event_base_dispatch(base);
- return 0;
- }
更多的 bufferevent API
释放 bufferevent
- // 如果存在未完成的延时回调,bufferevent 会在回调完成后才被真正释放
- void bufferevent_free(struct bufferevent *bev);
bufferevent 回调函数的设置和获取
- // 读取、写入回调函数原型
- // ctx 为用户自定义数据(由 bufferevent_setcb 设定)
- typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx);
- // 事件回调函数原型
- // ctx 为用户自定义数据(由 bufferevent_setcb 设定)
- // events 选项可以为:
- // BEV_EVENT_READING --- 在 bufferevent 上进行读取操作时出现了一个事件
- // BEV_EVENT_WRITING --- 在 bufferevent 上进行写入操作时出现了一个事件
- // BEV_EVENT_ERROR --- 进行 bufferevent 操作时(例如调用 bufferevent API)出错,获取详细的错误信息使用 EVUTIL_SOCKET_ERROR()
- // BEV_EVENT_TIMEOUT --- 在 bufferevent 上出现了超时
- // BEV_EVENT_EOF --- 在 bufferevent 上遇到了文件结束符
- // BEV_EVENT_CONNECTED --- 在 bufferevent 上请求连接完成了
- typedef void (*bufferevent_event_cb)(struct bufferevent *bev, short events, void *ctx);
- // 设置回调函数
- // 如果希望禁用回调函数,那么设置对应的参数为 NULL
- void bufferevent_setcb(
- // bufferevent
- struct bufferevent *bufev,
- // 读取回调函数
- bufferevent_data_cb readcb,
- // 写入回调函数
- bufferevent_data_cb writecb,
- // 事件回调函数
- bufferevent_event_cb eventcb,
- // 用户定义的数据
- // 这三个回调函数均共享此参数
- void *cbarg
- );
- // 取回回调函数
- // 参数为 NULL 表示忽略
- void bufferevent_getcb(
- struct bufferevent *bufev,
- bufferevent_data_cb *readcb_ptr,
- bufferevent_data_cb *writecb_ptr,
- bufferevent_event_cb *eventcb_ptr,
- void **cbarg_ptr
- );
设置 watermark
- // events 参数可以为
- // EV_READ 表示设置 read watermark
- // EV_WRITE 表示设置 write watermark
- // EV_READ | EV_WRITE 表示设置 read 以及 write watermark
- void bufferevent_setwatermark(struct bufferevent *bufev, short events,
- size_t lowmark, size_t highmark);
获取到输入和输出 buffer
- // 获取到输入 buffer
- struct evbuffer *bufferevent_get_input(struct bufferevent *bufev);
- // 获取到输出 buffer
- struct evbuffer *bufferevent_get_output(struct bufferevent *bufev);
添加数据到输出 buffer
- // 下面两个函数执行成功返回 0 失败返回 -1
- // 向 bufev 的输出 buffer 中添加大小为 size 的数据
- // 数据的首地址为 data
- int bufferevent_write(struct bufferevent *bufev,
- const void *data, size_t size);
- // 向 bufev 的输出 buffer 中添加数据
- // 数据来源于 buf
- // 此函数会清除 buf 中的所有数据
- int bufferevent_write_buffer(struct bufferevent *bufev,
- struct evbuffer *buf);
从输入 buffer 中获取数据
- // 从 bufev 的输入 buffer 中获取最多 size 字节的数据保存在 data 指向的内存中
- // 此函数返回实际读取的字节数
- size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
- // 获取 bufev 的输入 buffer 中的所有数据并保存在 buf 中
- // 此函数成功返回 0 失败返回 -1
- int bufferevent_read_buffer(struct bufferevent *bufev,
- 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
(转)Libevent(4)— Bufferevent的更多相关文章
- 处理大并发之五 使用libevent利器bufferevent
转自:http://blog.csdn.net/feitianxuxue/article/details/9386843 处理大并发之五 使用libevent利器bufferevent 首先来翻译一段 ...
- libevent(十)bufferevent 2
接上文libevent(九)bufferevent 上文主要讲了bufferevent如何监听读事件,那么bufferevent如何监听写事件呢? 对于一个fd,只要它的写缓冲区没有满,就会触发写事件 ...
- libevent(九)bufferevent
bufferevent,带buffer的event struct bufferevent { struct event_base *ev_base; const struct bufferevent_ ...
- libevent+bufferevent总结
libevent+bufferevent总结 1 学习参考网址 libevent学习网址:http://blog.csdn.net/feitianxuxue/article/details/93725 ...
- libevent中的bufferevent原理
以前的文章看过缓冲区buffer了,libevent用bufferevent来负责管理缓冲区与buffer读写事件. 今天就带大家看下evbuffer.c,使用bufferevent处理事 ...
- 项目中的Libevent(多线程)
多线程版Libevent //保存线程的结构体 struct LibeventThread { LibEvtServer* that; //用作传参 std::shared_ptr<std::t ...
- reactor模型框架图和流程图 libevent
学习libevent有助于提升程序设计功力,除了网络程序设计方面外,libevent的代码里有很多有用的设计技巧和基础数据结构,比如信息隐藏.函数指针.c语言的多态支持.链表和堆等等,都有助于提升自身 ...
- libevent(1)
很多时候,除了响应事件之外,应用还希望做一定的数据缓冲.比如说,写入数据的时候,通常的运行模式是: l 决定要向连接写入一些数据,把数据放入到缓冲区中 l 等待连接可以写入 l 写入尽量多的数据 l ...
- libevent网络编程汇总
libevent源码剖析: ========================================================== 1.libevent源码剖析一(序) 2.libeve ...
- Libevent:7Bufferevents基本概念
很多时候,应用程序除了能响应事件之外,还希望能够处理一定量的数据缓存.比如,当写数据的时候,一般会经历下列步骤: l 决定向一个链接中写入一些数据:将数据放入缓冲区中: l 等待该链接变得可写: ...
随机推荐
- 搭建集群必备:windows如何使用Xshell远程连接(SSH)Linux
出处about云(http://www.aboutyun.com/blog-61-22.html)欢迎访问我的博客 首先介绍一下环境: (主机)操作系统:win7 虚拟机:vmware worksta ...
- 【ACM/ICPC2013】POJ基础图论题简析(一)
前言:昨天contest4的惨败经历让我懂得要想在ACM领域拿到好成绩,必须要真正的下苦功夫,不能再浪了!暑假还有一半,还有时间!今天找了POJ的分类题库,做了简单题目类型中的图论专题,还剩下二分图和 ...
- mongodb基础系列——数据库查询数据返回前台JSP(一)
经过一段时间停顿,终于提笔来重新整理mongodb基础系列博客了. 同时也很抱歉,由于各种原因,没有及时整理出,今天做了一个demo,来演示,mongodb数据库查询的数据在JSP显示问题. 做了一个 ...
- JS自定义事件(Dom3级事件下)
原文出处: http://www.w3cfuns.com/notes/11861/e21736a0b15bceca0dc7f76d77c2fb5a.html . 我拿出作者中的一段,感谢作者原创. ...
- <a href> 带有cookie
<a href = <s:url action="exam/examAction_startExam.action" > <s:param name=&qu ...
- mydumper工作原理 :myownstars专家
http://blog.itpub.net/15480802/viewspace-1465080/
- gallery左右滑动时图片淡入淡出
前几天,公司项目有一个功能要做成滑动图片的淡入淡出,要一边滑动一边改变,所以ViewFlipper左右滑动效果就不能了.网上找了很久,也找不到资料,所以自己写了一个,通过滑动改变imageView的透 ...
- shell 实例脚本
例1: #!/bin/bashsum=0;for i in {1..100..2}do let "sum+=i"doneecho "the sum is $sum&quo ...
- j2ee学习笔记 javascript 学习
JavaScript 组成: ECMAScript + BOM + DOM Window对象是JS中的顶层对象 ECMAScript: 规定了一些语法,变量,for循环等等结构 BOM: Browse ...
- 微信小程序的一些限制
小程序的一些限制: 不支持HTML.没有 Dom.网页用的 JS.CSS 基本要全部重写,WXML 的语法和 HTML 差异还挺大,基本是一个个照着手册的属性去改.CSS 选择器不支持级联. 小程序源 ...