libevent简介
libevent
是一个用C语言编写的轻量级的开源高性能事件通知库,支持多种I/O多路复用技术,支持定时器和信号等事件,而且是跨平台的。
libevent特点
- 事件驱动,高性能
- 轻量级,专注于网络
- 跨平台,支持
Windows
、Linux
、MacOs
等 支持多种I/O多路复用技术,
epoll
、poll
、dev/poll
、select
和kqueue
等支持I/O,定时器和信号等事件
libevent下载及安装
这里我下载的是libevent-2.1.8-stable.tar.gz
,下载之后解压。
1 | tar -zxvf libevent-2.1.8-stable.tar.gz |
接下来进行源码包安装,这一步的目的是检查安装环境,生成makefile
1 | ./configure |
之后执行make
命令生成.o
可执行文件,最后执行sudo make install
将必要资源放置系统指定目录
验证libevent安装
进入libevent-2.1.8-stable
下的sample
目录,gcc编译hello-world.c
文件
1 | gcc hello-world.c -o hello |
这个时候会出现报错如下,显示未定义引用
出现上述报错是因为我们没有指定库名,因此需要加-l
选项
1 | gcc hello-world.c -o hello -l event |
hello
文件是一个简单的服务器程序,端口是9995
,我们尝试运行hello
,它会等待客户端的连接。并向客户端发送Hello,World
字符串,如下所示,证明libevent
库安装成功。
libevent
库的安装位置在/usr/local/lib
路径下
Hello-world分析
1 | /* |
libevent框架
创建event_base
1 |
|
创建事件
- 常规事件
event
—->event_new()
- 带缓冲区的事件
bufferevent
—->bufferevent_socket_new()
添加事件
1 | int event_add(struct event *ev,const struct timeval *tv); |
启动循环
1 | int event_base_dispatch(struct event_base *base); |
只有event_new
中指定了EV_PERSIST
才持续触发,否则只触发一次,就跳出循环
通常设置为EV_WRITE|EV_PERSIST
、EV_READ|EV_PERSIST
其他循环
在指定时间后停止循环
1 | int event_base_loopexit(struct event_base *base,const struct timeval *tv); |
立即停止循环
1 | int event_base_loopbreak(struct event_base *base); |
释放event_base
1 | event_base_free(base); |
查看支持哪些多路I/O
event.c
代码如下
1 |
|
gcc编译
1 | gcc event.c -o event -l event |
执行结果如下,可以发现mac并不支持epoll
1 | caoyifan@MacBookPro event % ./event |
创建事件
1 | struct event *event_new(struct event_base *base,evutil_socket_t fd,short what,event_callback_fn cb,void *arg); |
添加事件到base
1 | int event_add(struct event *ev,const struct timeval *tv); |
从base拿下事件
1 | int event_del(struct event *ev) |
销毁事件
1 | int event_free(struct event *ev) |
使用libevent读写fifo
read_fifo.c
代码如下
1 |
|
write_fifo.c
代码如下
1 |
|
在mac上编译执行之后发现并没有打印结果,gdb
调试一下,发现卡在了dispatch
这里,dispatch
函数就相当于是while+select/poll/epoll
这种形式,对于libevent
库默认使用的是epoll
,然而epoll
对于mac是不支持的。
于是我把代码放到了centos
服务器上,./read_fifo
运行报错如下
1 | error while loading shared libraries: libevent-2.1.so.6: cannot open shared object file: No such file or directory |
ldd查看依赖发现没有找到libevent-2.1.so.6
1 | elssm@VM-20-13-centos ~/event> ldd read_fifo |
创建软链接如下
1 | sudo ln -s /usr/lib/libevent-2.1.so.6 /usr/lib64/libevent-2.1.so.6 |
再次查看依赖
1 | elssm@VM-20-13-centos ~/event> ldd read_fifo |
之后分别执行./read_fifo
和./write_fifo
。发现成功读写
事件的未决和非未决
- 未决:有资格被处理,但尚未被处理
- 非未决:没有资格被处理
事件的未决和非未决状态转换图如下所示
bufferevent
1 |
bufferevent
有两个缓冲区,也是队列实现,先进先出
读:有数据——>读回调函数被调用——->使用bufferevent_read()
——>读数据
写:使用bufferevent_write()
——->向写缓冲中写数据——->该缓冲区有数据自动写出——>写完,回调函数被调用
bufferevent创建和释放
创建bufferevent
1 | struct bufferevent *ev |
释放bufferevent
1 | void bufferevent_free(struct bufferevent *bev) |
给读写缓冲区设置回调
1 | void bufferevent_setcb(struct bufferevent *bufev,bufferevent_data_cb readcb,bufferevent_data_cb writecb,bufferevent_event_cb eventcb,void *cbarg); |
readcb对应的回调函数
1 | typedef void(*bufferevent_data_cb)(struct bufferevent *bev,void *ctx); |
例如
1 | void read_cb(struct bufferevent *bev,void *arg){ |
1 | size_t bufferevent_read(struct bufferevent *bufev,void *data,size_t size); |
writecb对应的回调函数
1 | void write_cb(struct bufferevent *bev,void *arg){ |
1 | int bufferevent_write(struct bufferevent *bufev,const void *data,size_t size); |
eventcb对应的回调函数
1 | typedef void(*bufferevent_event_cb)(struct bufferevent *bev,short events,void *ctx); |
禁用和启用缓冲区
默认:新建的bufferevent
写缓冲时enable
的,而读缓冲是disable
的
1 | void bufferevent_enable(struct bufferevent *bufev,short events); |
1 | void bufferevent_disable(struct bufferevent *bufev,short events); //禁用 |
1 | short bufferevent_get_enabled(struct bufferevent *bufev); |
客户端连接服务器
1 | int bufferevent_socket_connect(struct bufferevent *bev,struct sockaddr *address,int addrlen); |
服务器创建监听器
1 |
|
1 | struct evconnlistener *listener |
回调函数类型
1 | typedef void(*evconnlistener_cb)( |
释放监听服务器
1 | evconnlistener_free(listener); |
服务端bufferevent创建TCP连接流程
- 创建
event_base
- 创建服务器连接监听器
evconnlistener_new_bind()
- 在
evconnlistener_new_bind()
的回调函数中,处理接受连接后的操作 - 回调函数被调用,说明有一个新的客户端连接,会得到一个新的fd,用于和客户端进行通信
- 创建
bufferevent
事件对象,bufferevent_socket_new()
,将fd封装到这个事件对象中 - 使用
bufferevent_setcb()
函数给bufferevent
的read、write、event
设置回调函数 - 设置读缓冲、写缓冲的使能状态
enable、disable
- 接受、发送数据
bufferevent_read()/bufferevent_write()
- 启动循环
event_base_dispatch()
- 释放资源
服务端bufferevent实现
1 |
|
编译
1 | gcc ev_server.c -o ev_server -l event |
客户端bufferevent创建TCP连接流程
- 创建
event_base
- 使用
bufferevent_socket_new()
创建一个用于跟服务器通信的bufferevent
事件对象 - 使用
bufferevent_socket_connect()
连接服务器 - 使用
bufferevent_setcb()
给bufferevent
对象的read、write、event
设置回调 - 设置
bufferevent
对象的读写缓冲区enable/disable
- 接受、发送数据
bufferevent_read()/bufferevent_write()
- 启动循环监听
event_base_dispatch()
- 释放资源
客户端bufferevent实现
1 |
|
编译
1 | gcc ev_client.c -o ev_client -l event |