libevent介绍
libevent 是一个开源的事件通知库,它提供了一个跨平台的抽象接口,libevnet处理的事件包括网络IO事件,定时事件以及信号事件。它可以在不同的操作系统上使用,包括Linux、Windows和Mac OS X等。libevent 的主要目的是提供高效的事件通知机制,用户无需关注平台检测处理事件的机制的差异,只需关注事件的具体处理。它可以用于编写服务器、客户端和其他网络应用程序。
libevent使用
网络中有很多说明libevent如何编译的文章,我就不介绍libevent的如何编译安装,首先我们使用一段代码来直观的展示libevent是如何使用的,然后在介绍libevent的流程和libevent的关键API。下面这段代码展示的是服务器接收客户端的消息,并返回消息给客户端。
#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include "event2/event.h"
void socket_read_cb(int fd, short events, void *arg);
//接收到客户端的回调处理
void socket_accept_cb(int fd, short events, void* arg)
{
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
evutil_socket_t clientfd = accept(fd, (struct sockaddr*)&addr, &len);
evutil_make_socket_nonblocking(clientfd);//设置客户端socket为非阻塞模式
printf("accept a client %d\n", clientfd);
struct event_base* base = (struct event_base*)arg;
struct event *ev = event_new(NULL, -1, 0, NULL, NULL);//分配一个事件
event_assign(ev, base, clientfd, EV_READ | EV_PERSIST,
socket_read_cb, (void*)ev);
event_add(ev, NULL);//将事件加入到事件管理器中进行管理
}
//客户端发送数据的回调处理
void socket_read_cb(int fd, short events, void *arg)
{
char msg[4096];
struct event *ev = (struct event*)arg;
int len = read(fd, msg, sizeof(msg) - 1);
if( len <= 0 )
{
printf("client fd:%d disconnect\n", fd);
event_free(ev);
close(fd);
return;
}
msg[len] = '\0';
printf("recv the client msg: %s", msg);
char reply_msg[4096] = "recvieced msg: ";
strcat(reply_msg + strlen(reply_msg), msg);
write(fd, reply_msg, strlen(reply_msg));
}
int socket_listen(int port)
{
int errno_save;
evutil_socket_t listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd == -1)
return -1;
evutil_make_listen_socket_reuseable(listenfd);//设置端口和地址为复用
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = 0;
sin.sin_port = htons(port);
if (bind(listenfd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
evutil_closesocket(listenfd);
return -1;
}
if (listen(listenfd, 5) < 0) {
evutil_closesocket(listenfd);
return -1;
}
evutil_make_socket_nonblocking(listenfd);//设置socket未非阻塞模式
return listenfd;
}
int main(int argc, char** argv)
{
int listenfd = socket_listen(8080);
if (listenfd == -1)
{
printf("socket_listen error\n");
return -1;
}
struct event_base* base = event_base_new();//创建事件管理器对象
struct event* ev_listen = event_new(base, listenfd, EV_READ | EV_PERSIST,
socket_accept_cb, base);//创建监听事件对象
event_add(ev_listen, NULL);//将监听事件对象加入到事件管理器中进行管理
event_base_dispatch(base);//消息循环(代码会被阻塞到此处)
return 0;
}
libevent流程
Libevent框架本质上是一个典型的Reactor模式,所以只需要弄懂Reactor模型,libevent就八九不离十了。
Reactor模式,是一种事件驱动机制。应用程序需要提供相应的接口并注册到Reactor上,如果相应的事件发生,Reactor将主动调用应用程序注册的接口,这些接口又称为“回调函数”。
在Libevent中也是一样,向Libevent框架注册相应的事件和回调函数;当这些事件发生时,Libevent会调用这些回调函数处理相应的事件(I/O读写、定时和信号)。
(1) 创建事件管理器对象
(2) 创建事件对象,并将事件加入到事件管理器对象中
(3) 消息事件循环
在网络事件框架中,我们主要关注2个问题:事件的检测和事件的处理,同样,libevent也主要关注这2个方面,我们使用libevent,可以仅仅使用libevent的事件检测,事件处理的逻辑由我们自己来写,或者事件检测和事件处理都使用libevent提供的方式。
(1)事件的检测
事件管理器对象负责检测事件是否发生,比如是否发生了IO事件(比如是否有客户端的连接,客户端是否发送了数据),定时器事件是否超时等。
(2)事件的处理
当检测到事件发生时,我们应该对事件进行处理,以下是一些示例
a.连接建立时:构建黑白名单,限制最大连接数
b.连接断开时:释放连接和对应的资源
c.数据到达时:数据的处理
因此我们将libevent的API分为这2个方面
libevent API
事件检测相关
事件管理器API
event_base_new();//创建事件管理器对象
event_base_new_with_config();//带配置的事件管理器对象
event_base_free();//销毁事件管理器对象
event_base_loop();//事件循环
event_base_loopbreak();//事件循环退出
event_base_loopexit();//事件循环退出
事件API
event_new();//构建事件对象
event_free();//销毁事件对象
event_add();//注册事件
event_del();//注销事件
event_assign();//修改事件
事件操作API
连接接收
事件监听器对象用于服务器监听客户端的对象,对象为evconnlisterner
evconnlisterner_new();//创建事件监听器对象
evconnlisterner_new_bind();//创建事件监听器对象
evconnlisterner_free();//销毁事件监听器对象
evconnlisterner_set_error_cb();//设置错误回调
读,写,连接操作
libevent的读写和客户端连接操作主要使用bufferevent对象来进行操作的。
bufferevent_socket_new();//创建bufferevent对象
bufferevent_setcb();//设置bufferevent对象的事件回调(读,写,错误回调)
bufferevent_enable();//注册事件类型
bufferevent_disable();//注销事件类型
bufferevent_get_input();//获取读缓冲区
bufferevent_get_output();//获取写缓冲区
bufferevent_read();//读缓冲区
bufferevent_write();//写缓冲区
bufferevent_socket_connect();//连接操作