(转)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 等待该链接变得可写: ...
随机推荐
- git 初级
以前工作中用到git,但没有总结,这次借鉴其它博客加上自己实践,总结git的简单用法 首先安装.... 打开一个文件右击git bash 弹出来一个jit界面 git config http.post ...
- opencv基础知识-videowriter
一.前言-简介 在试验中需要常常将实验结果进行保存,在opencv中提供很好用的录制视频的句柄,也可称之为类-videowriter. videowriter应用那是相当的简单,总之分为三步: //声 ...
- Join-Path(拼接路径)
$a="d:" $a="d:\ab" $b="abcd" $c="m.txt" @($a,$b,$c) -join '\ ...
- Hadoop-2.2.0中文文档——MapReduce 下一代 -——集群配置
目的 这份文档描写叙述了怎样安装.配置和管理从几个节点到有数千个节点的Hadoop集群. 玩的话,你可能想先在单机上安装.(看单节点配置). 准备 从Apache镜像上下载一个Hadoop的稳定版本号 ...
- 关于WinRT中c++和c#相互调用的问题
先说结论(不见得是最终正确的结论,不过google了一晚上也没有查出个所以然来,即便有解决方法我也认为是微软傻x): 首先c#和c++理所应当的不应该在同一个工程中,而只能是同一个工程的两个项目.只能 ...
- Windows Mobile 6.0 SDK和中文模拟器下载
[转] Windows Mobile 6.0 SDK和中文模拟器下载 Windows Mobile 6.5 模拟器 2010年12月06日 星期一 07:48 转载自 zhangyanle86 终于编 ...
- How To Install Kernel 3.10 On Ubuntu, Linux Mint, Debian and Derivates
n this article I will show you how to install Linux Kernel 3.10 on Ubuntu 13.10 Saucy Salamander, Ub ...
- Android开发之位置定位详解与实例解析(GPS定位、Google网络定位,BaiduLBS(SDK)定位)
在android开发中地图和定位是很多软件不可或缺的内容,这些特色功能也给人们带来了很多方便.定位一般分为三种发方案:即GPS定位.Google网络定位以及基站定位 最简单的手机定位方式当然是通过GP ...
- Nginx高性能服务器安装、配置、运维 (4) —— Nginx服务、架构及其信号
五.Nginx服务.架构及其信号 (1)Nginx服务的查看 1.netstat -antp 查看Nginx是否在80端口运行: 2.ps aux|grep nginx 查看nginx相关进程: 发现 ...
- Base62编码与62进制
Base62编码 Base62编码与Base64编码类似,都用于数据内容编码.基本原理请参看<Base64算法>. import java.io.ByteArrayOutputStream ...