I/O框架库是以库函数的形式,封装了较为底层的系统调用,给应用程序提供的一组更便于使用的接口。
I/O框架库的实现原理:
(1)以Reactor模式实现。(2)以Proactor模式实现。(3)同时用Reactor和Proactor两种模式实现
基于Reactor模式的I/O框架库包括以下几个组件:
(1)句柄(Handle):I/O框架库要处理的对象,即I/O事件、信号和定时事件,统一称为事件源。-一个事件源通常和一个句柄绑定在一起。 句柄的作用是,当内核检测到就绪事件时,它将通过句柄来通知应用程序这一事件。在Linux环境下,I/O事件对应的句柄为文件描述符,信号事件对应的句柄为信号值。
(2)事件多路分发器(EventDemultiplexer):事件的到来是随机的、异步的。我们无法预知程序何时收到一个客户连接请求,又亦或收到一个暂停信号。所以程序需要循环地等待并处理事件,这就是事件循环。在事件循环中,等待事件一般使用I/O复用技术来实现。I/O 框架库一般将系统支持的各种I/O复用系统调用封装成统一的接口,称为事件多路分发器。事件多路分发器的demultiplex方法是等待事件的核心函数,其内部调用的是select、 poll、 epoll_wait 等函数。
此外,事件多路分发器还需要实现register_event和remove_event方法,以供调用者往事件多路分发器中添加事件和从事件多路分发器中删除事件。
(3)事件处理器和具体事件处理器(EventHandle and ConcreteEventHandle):事件处理器执行事件对应的业务逻辑。它通常包含一个或多个handle_event 回调函数,这些回调函数在事件循环中被执行。I/O 框架库提供的事件处理器通常是一个接口, 用户需要继承它来实现自己的事件处理器,即具体事件处理器。因此,事件处理器中的回调函数一般被声明为虚函数,以支持用户的扩展。 此外,事件处理器一般还提供一个get_handle方法,它返回与该事件处理器关联的句柄。那么,事件处理器和句柄有什么关系?当事件多路分发器检测到有事件发生时,它是通过句柄来通知应用程序的。因此,我们必须将事件处理器和句柄绑定,才能在事件发生时获取到正确的事件处理器。
(4)Reactor:Reactor是I/O框架库的核心。它提供的几个主要方法是:
1)handle_events 该方法执行事件循环。它重复如下过程:等待事件,然后依次处理所有就绪事件对应的事件处理器。
2)register_handler 该方法调用事件多路分发器的register_event 方法来往事件多路分发器中注册一个事件。
3)remove_handler 该方法调用事件多路分发器的remove_ event 方法来删除事件多路分发器中的一个事件。
作为一个I/O框架库,Libevent 具有如下特点:
(1)跨平台支持(Libevent支持 Linux、UNIX 和 Windows)
(2)统一事件源(Libevent对I/O事件、信号和定时事件提供统一的处理)
(3)线程安全(Libevent 使用libevent_pthreads 库来提供线程安全支持)
(4)基于Reactor模式的实现
下面通过代码实现来具体分析Libeven框架库的使用:
//I/O框架库libevent实现TCP链接
#include
#include
#include
#include
#include
#include
#include //libevent 库函数
#include
#include
#include
#include
#define MAXFD 30
struct event* event_arr[MAXFD]; //创建事件数组,保存事件
int create_socket(); //create_socket()函数声明
//arg 参数则是Reactor传递给回调函数的参数。
//recv_cb为回调函数,参照56行代码
//event_new()函数的参数为此的函数指针 void (*cb) (evutil_socket_t, short, void* )
void recv_cb(int fd,short ev,void *arg) //TCP的recv()处理
{
if(ev & EV_READ)
{
char buff[128] = {0};
int n = recv(fd,buff,127,0);
if(n <= 0)
{
event_free(event_arr[fd]);
event_arr[fd] = NULL;
close(fd);
printf("one clinet end\n");
return ;
}
printf("recv(%d):%s\n",fd,buff);
send(fd,"ok",2,0);
}
}
//arg 参数则是Reactor传递给回调函数的参数。
//accept_cb为回调函数,参照107行代码
//event_new()函数的参数为此类型的函数指针 void (*cb) (evutil_socket_t, short, void* )
void accept_cb(int fd,short ev,void *arg) //TCP的accept()处理
{
if(ev & EV_READ)
{
struct sockaddr caddr;
int len = sizeof(caddr);
int c = accept(fd,(struct sockaddr*)&caddr,&len);
if(c<0)
{
return ;
}
printf("accept c = %d\n",c);
//使用Reactor传递给回调函数的参数base,参照107行代码
struct event_base* base = (struct event_base*)arg;
struct event* c_event = event_new(base,c,EV_READ | EV_PERSIST,recv_cb,NULL);//定义c可读事件(永久事件)
assert(c_event != NULL);
event_add(c_event,NULL);
event_arr[c] = c_event; //将c生成的事件保存下来
}
}
//主函数
int main()
{
int sockfd = create_socket();
assert( sockfd != -1);
int i = 0;
for(;i