8.libevent
1.简介
- 开源的库,提高开发效率
- 封装了socket通信
- 封装了IO多路转接
- 精简,专注于网络,性能高
- 事件驱动
2.libeveent库的安装
- 官方网站:http://libevent.org
- 安装
- 解压:
tar zxvf lib......
./configure
--prefix == /user/xxx
- 检测安装环境
- 生成
makefile
make
- 编译源代码
- 生成一些库
- 动态静态
- 可执行程序
sudo make install
- 将数据拷贝到对应的目录
- 如果目录不存在,创建该目录
- 默认目录
/usr/local
/usr/local/include
/usr/local/bin
/usr/local/lib
- 解压:
- 动态库找不到的问题
- 将
xxx.so
放到环境变量中LD_LIBRARY_PATH
export LD_LIBRARY_PATH = XXX
~/.bashrc
– 用户级别/etc/protile
–系统级别
- 使用命令重新加载
.~/.bashrc
./etc/profile
.
等价于source
- 修改/etc/ls.so.conf
- 动态库路径添加到该文件中 - 绝对路径
sudo ldconfig -v
- 将
- 库的使用
- 编译程序指定 -
levent
- 常用头文件
#include <event2/event.h>
#include <event2/listener.h>
- 编译程序指定 -
3.使用过程
- 创建一个事件处理框架 –
event_base
- 使用
libevent
函数之前需要分配一个或者多个event_base
结构题。每个event_base
结构体都有一个事件集合,可以检测哪个事件是激活的。 - 创建
event_base
struct event_base* event_base_new(void);
- 失败返回
NULL
- 释放event_base
event_base_free(struct event_base* base);
- 循环监听base对应的事件,等待条件满足
event_base_dispatch();
- 查看event_base封装的后端
const char **event_get_supported_methods(void);
char* str[];
const char *event_base_get_method(const struct event_base *base);
event_base
和fork()
- 子进程创建成功之后,父进程可以继续使用
event_base
- 子进程中需要继续使用
event_base
需要重新进程初始化int event_reinit(struct event_base* base);
- 子进程创建成功之后,父进程可以继续使用
- 使用
- 创建一个事件
event_new()
- 设置未决事件
- 构造事件之后,再将其添加到event_base之前实际上是不能对其做任何操作的。
int event_add(struct event *ev, const struct timeval *tv);
tv
:NULL
:事件被触发,对应的回调被调用tv={0,100}
,如果设置的时间在改时间段内检测的事件没被触发,时间到达之后,回调函数还是会被调用
- 函数调用成功返回0,失败返回-1
- 设置非未决
int event_del(struct event *ev);
一些宏
#define EV_TIMEOUT 0x01 //废弃
#define EV_READ 0x02
#define EV_WRITE 0x04
#define EV_SIGNAL 0x08
#define EV_PERSIST 0x10 //持续触发
#define EV_ET 0x20 //边沿模式
- 事件添加到事件处理框架上
int event_base_loop(struct event_base *base, int flage);
- 正常退出返回0,失败返回-1
int event_base_dispatch(struct event_base *base);
- 等同于没有设置标志的
event_base_loop()
- 将一直运行,直到没有已经注册的事件了,或者调用了
event_base_loopbreak()
或者event_base_loopexit()
为止。
- 等同于没有设置标志的
- 循环停止
- 返回值:成功0,失败-1
- 开始事件循环
- 释放资源
- 释放事件:
int event_free(struct event *event);
- 释放事件:
4. Bufferevent
- 数据缓冲区
event2/bufferevent.h
bufferevent
理解:- 是
libeverent
为IO缓冲区操作提供的一种通用机制 bufferevent
由一个底层的传输端口(如套接字),一个读取缓冲区和一个写入缓冲区组成。- 与通常的事件在底层传输端口已经就绪,可以读取或者
- 是
- 回调 - 缓冲区对应的操作
- 每个
bufferevent
有两个数据相关的回调- 一个读取回调
- 从底层端口读取了任意量的数据之后会调用读取回调
- 一个写入回调
- 输出缓冲区中足够量的数据被清空到底层传输端口后写入回调会被调用
- 一个读取回调
- 每个
5.使用bufferevent
- 创建基于套接字的bufferevent
- 可以使用bufferevent_socket_new()创建基于套接字的bufferevent
struct bufferevent *bufferevent_socket_new( struct event_base *base, event_socket_t fd, enum bufferevent_options options);
options
:BEV_OPT_CLOSE_ON_FREE- 释放bufferevent时关闭底层传输端口。这将关闭底层套接字,释放底层的bufferevent等
- page53
struct bufferevent
- 成功时返回一个bufferevent,失败返回NULL
- 在bufferevent上启动链接
int bufferevent_socket_connect(struct bufferevent *bev), struct sockaddr *address, int addrlen);
- address 和 addrlen参数跟标准调用connect()参数相同。如果还没有为bufferevent设置套接字,调用函数将为其分配一个新的流套接字,并且设置为非阻塞
- 如果已经为bufferevent设置套接字,调用bufferevent_socket_connect将告知libevent套接字还未链接,直到链接成功前不应该对其进行读取和写入操作
- 链接完成之前可以向输出缓冲区添加数据
- 释放bufferevent操作
void bufferevent_free(struct bufferevent *bev);
- bufferevent读写缓冲区回调操作
typedef void(*bufferevent_data_cb)( struct bufferevent *bev, void *ctx);
typedef void(*bufferevent_event_cd)(struct bufferevent *bev, short event, void *ctx);
- event参数:
- EV_EVENT_READING:读取操作时发生某事件,具体是哪种事件请看其他标志。
BEV_EVENT_WRITING:写入操作时发生某事件,具体是哪种事件请看其他标志。
EVUTIL_SOCKET_ERROR()。
BEV_EVENT_ERROR:操作时发生错误。关于错误的更多信息,请调 用
BEV_EVENT_TIMEOUT:发生超时。
BEV_EVENT_EOF:遇到文件结束指示。 - BEV_EVENT_CONNECTED:请求的连接过程已经完成
- EV_EVENT_READING:读取操作时发生某事件,具体是哪种事件请看其他标志。
void bufferevent_setb(struct bufferevent *bufev, bufferevent_data_cb readcd, bufferevent_data_cb writecd, bufferevent_event_cb eventcb, void *cbarg);
readcb
:在读回调中读数据writecb
:可以设置为NULLeventcb
:可以设置为NULL
- 禁用、启用缓冲区
- 禁用之后,回调函数不再被调用
void bufferevent_enable(struct bufferevent *bufev, short event);
void bufferevent_disable(struct bufferevent *bufev, short events);
short bufferevent_get_enabled(struct bufferevent *bufev);
- 操作bufferevent中的数据
- 向bufferevent的输出缓冲区中添加数据
int bufferevent_write(struct bufferevent *bufev, const void *data, size_t size);
- 从bufferevent 的输入缓冲区中移除数据
size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
- 向bufferevent的输出缓冲区中添加数据
6.链接监听器
- 创建和释放evconnlistener
typedef void(*evconnlistener_cb)(struct evconnlistener *listenner, evutil_socket_t sock, struct sockaddr *addr, int len, void *ptr);
- sock : 用于通信的文件描述符
- addr : 客户端的IP和端口信息
- ptr : 外部传来的数据
struct evconnlistener *evconnlistener_new(struct event_base *base, evconnlistener_cb cb, void *ptr, unsigned flags, int backlog, evutil_socket_t fd);
- 没有包含bind
struct evconnlistener *evconnlistener_new_bind(struct event_base *base, evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,const struct sockaddr *sa, int socklen);
- cb:接受链接后,用户要做的操作
- backlog:使用默认的最大值
- sa:服务器的IP和端口信息
- 启用和禁用evconnlistener
int evconnlistener_disable(struct evconnlistener *lev);
int evconnlistener_enable(struct evconnlistener *lev);
- 调整evconnlistenner
void evconnlistener_set_cb(struct evconnlistener *lev, evconnlistener_cb cb,void *arg);
- 函数调整evconnlistener的回调函数和参数
code-server
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <event2/event.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>
// 读缓冲区回调
void read_cb(struct bufferevent *bev, void *arg)
{
char buf[1024] = {0};
bufferevent_read(bev, buf, sizeof(buf));
char* p = "我已经收到了你发送的数据!";
printf("client say: %s\n", p);
// 发数据给客户端
bufferevent_write(bev, p, strlen(p)+1);
printf("====== send buf: %s\n", p);
}
// 写缓冲区回调
void write_cb(struct bufferevent *bev, void *arg)
{
printf("我是写缓冲区的回调函数...\n");
}
// 事件
void event_cb(struct bufferevent *bev, short events, void *arg)
{
if (events & BEV_EVENT_EOF)
{
printf("connection closed\n");
}
else if(events & BEV_EVENT_ERROR)
{
printf("some other error\n");
}
bufferevent_free(bev);
printf("buffevent 资源已经被释放...\n");
}
void cb_listener(
struct evconnlistener *listener,
evutil_socket_t fd,
struct sockaddr *addr,
int len, void *ptr)
{
printf("connect new client\n");
struct event_base* base = (struct event_base*)ptr;
// 通信操作
// 添加新事件
struct bufferevent *bev;
bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
// 给bufferevent缓冲区设置回调
bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);
bufferevent_enable(bev, EV_READ);
}
int main(int argc, const char* argv[])
{
// init server
struct sockaddr_in serv;
memset(&serv, 0, sizeof(serv));
serv.sin_family = AF_INET;
serv.sin_port = htons(9876);
serv.sin_addr.s_addr = htonl(INADDR_ANY);
struct event_base* base;
base = event_base_new();
// 创建套接字
// 绑定
// 接收连接请求
struct evconnlistener* listener;
listener = evconnlistener_new_bind(base, cb_listener, base,
LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
36, (struct sockaddr*)&serv, sizeof(serv));
event_base_dispatch(base);
evconnlistener_free(listener);
event_base_free(base);
return 0;
}
code - client
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <event2/event.h>
#include <event2/bufferevent.h>
void read_cb(struct bufferevent *bev, void *arg)
{
char buf[1024] = {0};
bufferevent_read(bev, buf, sizeof(buf));
printf("Server say: %s\n", buf);
}
void write_cb(struct bufferevent *bev, void *arg)
{
printf("I am Write_cb function....\n");
}
void event_cb(struct bufferevent *bev, short events, void *arg)
{
if (events & BEV_EVENT_EOF)
{
printf("connection closed\n");
}
else if(events & BEV_EVENT_ERROR)
{
printf("some other error\n");
}
else if(events & BEV_EVENT_CONNECTED)
{
printf("成功连接到服务器, O(∩_∩)O哈哈~\n");
return;
}
bufferevent_free(bev);
printf("free bufferevent...\n");
}
void send_cb(evutil_socket_t fd, short what, void *arg)
{
char buf[1024] = {0};
struct bufferevent* bev = (struct bufferevent*)arg;
printf("请输入要发送的数据: \n");
read(fd, buf, sizeof(buf));
bufferevent_write(bev, buf, strlen(buf)+1);
}
int main(int argc, const char* argv[])
{
struct event_base* base;
base = event_base_new();
struct bufferevent* bev;
bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
// 连接服务器
struct sockaddr_in serv;
memset(&serv, 0, sizeof(serv));
serv.sin_family = AF_INET;
serv.sin_port = htons(9876);
evutil_inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr.s_addr);
bufferevent_socket_connect(bev, (struct sockaddr*)&serv, sizeof(serv));
// 设置回调
bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);
bufferevent_enable(bev, EV_READ | EV_PERSIST);
// 创建一个事件
struct event* ev = event_new(base, STDIN_FILENO,
EV_READ | EV_PERSIST,
send_cb, bev);
event_add(ev, NULL);
event_base_dispatch(base);
event_base_free(base);
return 0;
}
代码来自黑马视频