下载地址:https://libevent.org/
解压缩:tar zxvf libevent-2.1.8-stable.tar.gz
源码包安装:
测试是否安装成功:
进入 sample 目录中, 编译 .c 文件, 运行。
编译时,务必添加 指定库名的 选项 -levent
gcc hello-world.c -o hello -levent
安装成功后, 会在 系统的 /usr/local/lib 目录下,多出 libevent.so 和 libevent.a 库文件。
如果运行 ./hello-world 可执行文件,报出如下错误: error while loading shared libraries: libevent-2.1.so.6: cannot open shared object file: No such file or directory
解决方法
将库目录添加到 /etc/ld.so.cache 文件中
sudo vim /etc/ld.so.conf 文件,加入库所在路径即 /usr/local/lib/
执行 sudo ldconfig -v ,该命令会重建/etc/ld.so.cache 文件
创建base,使用 event_base_new()
#include
struct event_base *event_base_new(void);
struct event_base *base = event_base_new();
创建事件对象 event
// 常规事件 event
event_new();
// 带缓冲区的事件 bufferevent
bufferevent_socket_new();
添加事件到 base上
int event_add(struct event *ev, const struct timeval *tv);
循环监听事件满足
int event_base_dispatch(struct event_base *base);
event_base_dispatch(base);
释放 base 和事件对象
void event_base_free(struct event_base *base);
event_base_free(base);
struct event *event_new(struct event_base *base,evutil_socket_t fd,short
what,event_callback_fn cb; void *arg);
base: event_base_new() 返回值。
fd: 绑定到 event对象上的 文件描述符
what: fd 对应的监听事件 (read、write、expcet)
EV_READ 监听 一次 读事件
EV_WRITE 监听 一次 写事件
EV_PERSIST 持续触发。 结合 event_base_dispatch 函数使用。 保证持续循环。
可以使用 “|” 连接。EV_PERSIST|EV_READ 或 EV_PERSIST|EV_WRITE
cb: 一旦,事件满足监听条, 回调的函数。
typedef void (*event_callback_fn)(evutil_socket_t fd, short, void *)
arg: 回调函数的参数。
返回值:成功创建的 事件对象
// 添加事件
int event_add(struct event *ev, const struct timeval *tv);
ev: event_new() 的返回值。 ————【注意】不是 event_base !!!
tv: 通常 NULL
// 销毁事件
int event_free(struct event *ev);
成功: 0, 失败: -1
read_fifo.c
#include
#include
#include
#include
#include
#include
void read_cb(evutil_socket_t fd, short what, void* arg)
{
char buf[BUFSIZ];
bzero(buf,sizeof(buf));
read(fd, buf, sizeof(buf));
printf("what:%s, read:%s\n",what & EV_READ ? "read YES" : "read NO", buf);
sleep(1);
}
int main()
{
unlink("myfifo");
mkfifo("myfifo", 0664);
int fd = open("myfifo",O_RDONLY|O_NONBLOCK);
//创建base
struct event_base* base = event_base_new();
//创建事件
//struct event *event_new(struct event_base *base,evutil_socket_t fd,short
//what,event_callback_fn cb; void *arg);
struct event* ev = event_new(base, fd, EV_READ|EV_PERSIST, read_cb, NULL);
//添加事件
event_add(ev, NULL);
//循环监听
event_base_dispatch(base);
//释放事件和base
event_base_free(base);
event_free(ev);
return 0;
}
write_fifo.c
#include
#include
#include
#include
#include
#include
void read_cb(evutil_socket_t fd, short what, void* arg)
{
char buf[] = "hello libevent";
write(fd, buf, strlen(buf)+1);
sleep(1);
}
int main()
{
int fd = open("myfifo",O_WRONLY|O_NONBLOCK);
//创建base
struct event_base* base = event_base_new();
//创建事件
//struct event *event_new(struct event_base *base,evutil_socket_t fd,short
//what,event_callback_fn cb; void *arg);
struct event* ev = event_new(base, fd, EV_WRITE|EV_PERSIST, read_cb, NULL);
//添加事件
event_add(ev, NULL);
//循环监听
event_base_dispatch(base);
//释放事件和base
event_base_free(base);
event_free(ev);
return 0;
}
event_base_get_method(base); 函数可以返回当前base使用的多路IO转接方式
未决态:事件有资格被处理, 尚未被处理。
非未决态:事件没有资格被处理。
struct bufferevent *bufferevent_socket_new(struct event_base *base,
evutil_socket_t fd, enum bufferevent_options options);
base: struct event_base ———— “base底座”
fd: 要封装到 bufferevent 内的 fd
options:
BEV_OPT_CLOSE_ON_FREE。 作用:释放 bufferevent对象时, 同时释放内部封装的资源。
【注意】:bufferevent_socket_new函数内,没有指定 fd 对应的 回调函数。
void bufferevent_free(struct bufferevent *bev);
bev:bufferevent_socket_new 函数的返回值。
void bufferevent_setcb(struct bufferevent *bufev,
bufferevent_data_cb readcb,
bufferevent_data_cb writecb,
bufferevent_event_cb eventcb,
void *cbarg );
bufev: bufferevent_socket_new() 函数的返回值。
readcb: 设置 bufferevent 读缓冲,对应的回调。
如: read_cb(){ 用 bufferevent_read() 去读数据 }
writecb: 设置 bufferevent 写缓冲,对应的回调。 ———— 通常 NULL
如: write_cb(){ 给调用者,发送写成功的“通知” }
eventcb: 设置事件回调。 ———— 通常 NULL
对应: BEV_EVENT_CONNECTED:———— 用作客户端。
cbarg:上述回调函数的参数。
typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void * ctx);
// 示例:
void read_cb(struct bufferevent *bev, void *cbarg)
{
.....
bufferevent_read(); ———— 顶替read()
}
// bufferevent_read 函数原型
size_t bufferevent_read(struct bufferevent *bev, void *buf, size_t bufsize);
typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void * ctx);
// 示例:
void write_cb(struct bufferevent *bev, void *cbarg)
{
.....
数据写完了,这个函数才会被调用!!!
}
// bufferevent_write 函数原型
int bufferevent_write(struct bufferevent *bev, const void *buf, size_t size);
typedef void (*bufferevent_event_cb)(struct bufferevent *bev, short events, void *cbarg);
// 示例:
void event_cb(struct bufferevent *bev, short events, void *cbarg)
{
events: BEV_EVENT_CONNECTED:———— 用作客户端。
.....
当有状态、特殊状况产生时,该回调函数会被调用。
}
void bufferevent_enable(struct bufferevent *bufev, short events); 启用缓冲区
bufev: bufferevent_socket_new() 函数的返回值
events: EV_READ、EV_WRITE、EV_READ|EV_WRITE 对应读和写缓冲区的启用
void bufferevent_disable(struct bufferevent *bufev, short events);
原来:socket(); connect(); 现在 socket() + bufferevent_socket_connect()
int bufferevent_socket_connect(struct bufferevent *bev, struct sockaddr *address, int addrlen);
bev: bufferevent_socket_new() 函数的返回值
address/addrlen: 等同与 connect() 参2、3
原来:socket(); bind(); listen(); accept(); 现在:evconnlistener_new_bind()
// 这一个函数,相当于 socket(); bind(); listen(); accept(); 的作用。
#include
struct evconnlistener *evconnlistener_new_bind (
struct event_base *base,
evconnlistener_cb cb,
void *ptr,
unsigned flags,
int backlog,
const struct sockaddr *sa,
int socklen);
base:struct event_base --- “底座”
cb:回调函数
一旦,该函数被回调,说明客户端已经成功连接。回调函数内部,应与客户端进行数据通信。
ptr:回调函数的参数
flags:LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE
backlog:listen 函数的2参。 -1 表最大值。
sa: 服务端自己的地址结构
socklen: 服务端自己的地址结构的大小。
返回值:成功创建的监听器。
typedef void (*evconnlistener_cb) (
struct evconnlistener *listener,
evutil_socket_t sock,
struct sockaddr *addr,
int len,
void *ptr);
【注】:该回调函数,不由我们调用,是框架自动调用。因此,只需知晓参数含义即可。
void evconnlistener_free(struct evconnlistener *lev);
创建 base
绑定地址结构 用于 evconnlistener_new_bind() 函数创建监听器的传参
创建监听器 evconnlistener_new_bind()
监听器的回调函数用于进行通信,进行通信要使用 bufferevent 来监听读写和事件,所以 bufferevent的创建要在监听器的回调函数中。以下是创建监听器时的回调函数:
创建 bufferevent 事件 bufferevent_socket_new()
设置回调函数 bufferevent_setcb()
启动读回调 bufferevent_enable()
event_base_dispatch() 启动循环监听
释放监听器资源,释放base
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 8000
void read_cb(struct bufferevent *bev, void *cbarg)
{
char buf[BUFSIZ];
int n = bufferevent_read(bev, buf, sizeof(buf));
for(int i=0 ;i<n ;++i)
{
buf[i] = toupper(buf[i]);
}
write(STDOUT_FILENO, buf, n);
bufferevent_write(bev, buf, n);
sleep(1);
}
void write_cb(struct bufferevent *bev, void* cbarg)
{
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 listen_cb(struct evconnlistener *listener, evutil_socket_t sock, struct sockaddr *addr, int len, void *ptr)
{
//char cltip[16];
//printf("client accept ip:%s, port%d\n",
// inet_ntop(AF_INET, (struct sockaddr_in*)addr->sin_addr.s_addr, cltip, sizeof(cltip)),
// htons((struct sockaddr_in*)addr->sin_port));
struct event_base* base = (struct event_base*)ptr;
//创建bufferevent事件
struct bufferevent* bev = bufferevent_socket_new(base, sock, BEV_OPT_CLOSE_ON_FREE);
//设置回调
bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);
//启动读写缓冲区
bufferevent_enable(bev, EV_READ|EV_WRITE);
}
int main()
{
struct sockaddr_in srv_addr;
srv_addr.sin_family = AF_INET;
srv_addr.sin_port = htons(PORT);
srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//创建base
struct event_base* base = event_base_new();
//创建监听器
struct evconnlistener* listener = evconnlistener_new_bind(base, listen_cb, (void*)base,
LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, -1, (struct sockaddr*)&srv_addr, sizeof(srv_addr));
//循环监听
event_base_dispatch(base);
//释放
event_base_free(base);
evconnlistener_free(listener);
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#define PORT 8000
void read_cb(struct bufferevent* bev, void *cbarg)
{
char buf[BUFSIZ];
int n = bufferevent_read(bev, buf, sizeof(buf));
buf[n] = '\0';
printf("服务器say: %s", buf);
bufferevent_write(bev, buf, n); //将数据再写回服务器,做出一直发送的效果
}
void write_cb(struct bufferevent* bev, void *cbarg)
{
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");
}
else if(events & BEV_EVENT_CONNECTED)
{
printf("已经连接服务器...\\(^o^)/...\n");
return;
}
// 释放资源
bufferevent_free(bev);
}
void read_terminal(evutil_socket_t fd, short what, void *arg )
{
struct bufferevent* bev = (struct bufferevent*)arg;
char buf[BUFSIZ];
int n = read(fd, buf, sizeof(buf));
bufferevent_write(bev, buf, n);
sleep(1);
}
int main()
{
int cfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in srv_addr;
srv_addr.sin_family = AF_INET;
srv_addr.sin_port = htons(PORT);
inet_pton(AF_INET, "127.0.0.1", &srv_addr.sin_addr.s_addr);
//创建base
struct event_base* base = event_base_new();
struct bufferevent* bev;
//创建befferevent事件
bev = bufferevent_socket_new(base, cfd, BEV_OPT_CLOSE_ON_FREE);
//设置回调
bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);
//启用读缓冲区
bufferevent_enable(bev, EV_READ|EV_WRITE);
//连接服务器
bufferevent_socket_connect(bev, (struct sockaddr*)&srv_addr, sizeof(srv_addr));
struct event* ev = event_new(base, STDOUT_FILENO, EV_READ|EV_PERSIST, read_terminal, bev);
//添加事件
/vent_add(ev, NULL);
//循环监听
event_base_dispatch(base);
//释放bufferevent事件
event_free(ev);
//释放base
event_base_free(base);
return 0;
}
有关管道读写注意:当写端关闭,读端还在读的时候,无论阻塞和非阻塞read都会返回0,表示对端关闭