高性能I/O框架库libevent的介绍和使用

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 方法来删除事件多路分发器中的一个事件。

Libevent框架库:

作为一个I/O框架库,Libevent 具有如下特点:

      (1)跨平台支持(Libevent支持 Linux、UNIX 和 Windows)

      (2)统一事件源(Libevent对I/O事件、信号和定时事件提供统一的处理)

      (3)线程安全(Libevent 使用libevent_pthreads 库来提供线程安全支持)

      (4)基于Reactor模式的实现

下面通过代码实现来具体分析Libeven框架库的使用:

//I/O框架库libevent实现TCP链接

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/signal.h>
#include <event.h>    //libevent 库函数
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#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<MAXFD;i++)
    {
	event_arr[i] = NULL;
    }

    struct event_base* base = event_init();  //调用event_init()函数创建base对象,一个base相当于Reactor实例
    assert(base != NULL);
	
    /*
	创建具体的事件处理器,并设置它们所从属的Reactor实例。
	evsignal_new  和   evtimer_new   分别用于创建信号事件处理器和定时事件处理器,它们是定义在include/event2/event.h文件中的宏:
	
    #define evsignal_new(b, x, cb, arg)  event_new((b), (x), EV_SIGNALIEV_PERSIST,(cb), (arg)) //创建信号事件处理器
	#define evtimer_new(b, cb, arg)      event_new((b), -1, 0,(cb), (arg) )                   //创建定时事件处理器
	
	可见,它们的统一入口都是event_new函数,即用于创建通用事件处理器的函数。其定义是:
	
	struct event* event_new(struct event_ base* base, evutil_socket_t fd, short events,
	                        void (*cb) (evutil_socket_t, short, void* ),void* arg) 
	
	其中,basc参数指定新创建的事件处理器从属的Reactor。fd参数指定与该事件处理器关联的句柄。创建I/O事件处理器时,应该给fd 参数传递文件描述符值;
	创建信号事件处理器时,应该给fd参数传递信号值,比如代码清单12-1中的SIGINT;创建定时事件处理器时,则应该给fd参数传递-1。
	events 参数指定事件类型,其可选值都定义在incelude/event2/event.h文件中。
	
      #define EV_TIMEOUT  0x01   //定时事件
	  #define EV_READ     0x02   //可读事件
	  #define EV_WRITE    0x04   //可写事件
	  #define EV_SIGNAL   0x08   //信号事件
	  #define EV_PERSIST  0x10   //永久事件
	  //边沿触发事件,需要I/O复用系统调用支持,比如epoll 
	  #define EV_ET       0x20
	  
	 cb参数指定目标事件对应的回调函数,相当于事件处理器的handle_event方法。
	 arg 参数则是Reactor传递给回调函数的参数。
	 event_new函数成功时返回一个event类型的对象,也就是Libevent的事件处理器。
	*/
    struct event * sock_event = event_new(base,sockfd,EV_READ | EV_PERSIST,accept_cb,base);   //定义sockfd可读事件(永久事件)
	
    assert(sock_event != NULL);
    event_add(sock_event,NULL);

    event_base_dispatch(base);// 使用event_base_dispatch()执行事件循环
	//使用*_free()系列函数释放资源
    event_free(sock_event);  //释放
    event_base_free(base);  //释放
}

int create_socket()  //创建socket
{
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd == -1)
    {
	return -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));
    if(res == -1)
	{
		return -1;
	}

    listen(sockfd,5);

    return sockfd;
}

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值