一、I/O框架库概述:I/O框架库以库函数的形式,封装了较为底层的系统调用,给应程序提供了一组更便于使用的接口,这些库函数往往比程序员自己实现的同样功能的函数 更合理、更高效、更健壮,因为它们经受住了真实网络环境下的高压测试,以及时间的考验。下面我们以Reactor模式实现,基于Reactor模式的I/O框架库包含以下几个组件:句柄(描述符)、时间多路分发器、
1、句柄
I/O框架要处理的对象,即I/O事件、信号和定时事件,统一称为事件源。一个事件源通常和一个句柄绑定在一起,句柄的作用:当内核检测到就绪事件时,它将通过句柄来通知应用程序这一事件。在Linux环境下,I/O事件对应的句柄就是文件描述符,信号事件对应的句柄就是信号值。
2、事件多路分发器
事件的到来使随机的、异步的,我们无法预知程序何时收到一个客户连接请求,又亦或收到一个暂停信号。所以程序需要循环地等待并处理事件,这就是事件循环。在事件循环中,等待事件一般使用I/O复用技术来实现。I/O框架库一般将系统支持的各种I/O复用系统调用封装成统一的接口,称为事件多路分发器。事件多路分发器demultiplex方法是等待事件的核心函数,其内部调用的是select、poll、epoll_wait等函数
二、时间处理器和具体事件处理器
事件处理器执行事件对应的业务逻辑,它通常包含一个或多个handle_event回调函数,这些回调函数在事件循环中被执行。I/O框架提供的事件处理器通常是一个接口,用户需要继承它来实现自己的事件处理器,即具体事件处理器。因此,事件处理器中的回调函数一般被声明为虚函数,以支持用户的扩展。
小问号?事件处理器和句柄有什么关系?
当事件多路分发器检测到有事件发生时,它是通过描述符来通知应用程序的,因此,饿哦们必须将事件处理器和描述符绑定,才能在事件发生时获取到正确的事件处理器。
三、Reactor
Reactor是I/O框架库的核心,它主要提供三个主要的方法:
(1)handle_events.该方法执行事件循环。它重复如下过程:等待事件,然后依次处理所有就绪事件对应的事件处理器。
(2)register_handler.该方法调用事件多路分发器的register_event方法来往事件多路分发器中注册事件
(3)remove_handler.该方法调用事件多路分发器的remove_event方法来删除事件多路分发器中的一个事件
四、Libevent源码分析
Lievent具有以下的特点:
(1)跨平台支持,Lievent支持Linux、UNIX和Windows.
(2)统一事件源。Libevent对I/O事件、信号和定时事件提供统一的处理
(3)线程安全。Libevent使用libevent_pthreads库来提高线程安全支持
(4)基于Reactor模式的研究
代码实现:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <signal.h>
#include <event.h>
void sig_cb(int fd, short ev, void* arg)
{
printf("fd=%d\n",fd);
}
void timeout_cb(int fd, short ev, void* arg)
{
printf("time out\n");
}
int main()
{
struct event_base * base = event_init();
assert( base != NULL );
//创建信号事件处理器
//struct event * sig_ev = evsignal_new(base,SIGINT,sig_cb,NULL);
struct event * sig_ev = event_new(base,SIGINT,EV_SIGNAL,sig_cb,NULL);
assert( sig_ev != NULL );
event_add(sig_ev,NULL);
struct timeval tv = {3,0}; //创建定时事件处理器
//struct event * time_ev = evtimer_new(base,timeout_cb,NULL);
//struct event * time_ev = event_new(base,-1,EV_TIMEOUT|EV_PERSIST,timeout_cb,NULL);
struct event * time_ev = event_new(base,-1,EV_TIMEOUT,timeout_cb,NULL);
event_add(time_ev,&tv);
//调用该函数来执行事件循环
event_base_dispatch(base);
//事件结束后,使用*_free系列函数来释放系统资源
event_free(time_ev);
event_free(sig_ev);
event_base_free(base);
}
服务器代码实现:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <signal.h>
#include <event.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define MAXFD 20
struct event* fds_map[MAXFD];
void fds_map_init(struct event* fds[] )
{
int i = 0;
for( ;i < MAXFD; i++ )
{
fds[i] = NULL;
}
}
void fds_map_add(struct event * fds[], int fd, struct event* ev)
{
if ( fd >= MAXFD || ev == NULL)
{
return ;
}
fds[fd] = ev;
}
struct event* fds_map_find(struct event * fds[], int fd)
{
return fds[fd];
}
void fds_map_del(struct event * fds[], int fd )
{
if ( fd >= MAXFD )
{
return;
}
fds[fd] = NULL;
}
void c_cb(int fd, short ev, void* arg)
{
if ( ev & EV_READ )
{
char buff[128] = {0};
if ( recv(fd,buff,127,0) <= 0 )
{
event_free(fds_map_find(fds_map,fd));
fds_map_del(fds_map,fd);
close(fd);
printf("one client over\n");
return;
}
printf("buff=%s\n",buff);
send(fd,"ok",2,0);
}
}
void accept_cb(int fd, short ev, void * arg)
{
struct event_base * base = (struct event_base*)arg;
struct sockaddr_in caddr;
int len = sizeof(caddr);
if ( ev & EV_READ )
{
int c = accept(fd,(struct sockaddr*)&caddr,&len);
if ( c <0 )
{
return ;
}
struct event * ev_c = event_new(base,c,EV_READ|EV_PERSIST,c_cb,NULL);
event_add(ev_c,NULL);
fds_map_add(fds_map,c,ev_c);
}
}
int main()
{
//创建套接字
int sockfd = socket(AF_INET,SOCK_STREAM,0);
assert( sockfd != -1 );
struct sockaddr_in saddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(6000);
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
assert( res != -1 );
listen(sockfd,5);
fds_map_init(fds_map);
//调用event_init函数创建event_base对象,一个event_base相当于一个Reactor实例
struct event_base * base = event_init();
assert( base != NULL );
// event_new创建信号事件 //事件触发之后,自动重新对这个event调用event_add函数(永久性事件)
struct event* ev_sock = event_new(base,sockfd,EV_READ|EV_PERSIST,accept_cb,base);
//指定的回调函数
//调用event_add函数,将事件处理器添加到注册事件队列中,并将该事件处理器对应的事件添加到事件多路分发器中
event_add(ev_sock,NULL);
fds_map_add(fds_map,sockfd,ev_sock);
//调用该函数来执行事件循环
event_base_dispatch(base);
//事件结束后,使用*_free系列函数来释放系统资源
event_free(ev_sock);
event_base_free(base);
}
五、Lievent支持的事件类型