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();//连接操作