跨平台事件驱动 libevent 库从安装到简单使用

1.libevent 库的下载和安装

1.打开网址:http://libevent.org/ 下载最新版即可
在这里插入图片描述
2.源码包拷贝到Linux系统,解压之后执行以下操作:
2.1 ./config (安装环境检测) 生成makefile 文件
2.2 make 执行Makefile文件,
2.3 sudo make install 将数据拷贝到对应的目录当中

3.sample 示例
在libevent库里有着许多的sample示例程序,如http_serve 、hello-world、event-read,如下所示。

guoqi1@ubuntu:~/Desktop/libevent-2.1.11-stable/sample$ ls 
dns-example      event-read-fifo.c  hello-world.o  http-connect.c  http-server.c  openssl_hostname_validation.c  signal-test.o
dns-example.c    event-read-fifo.o  hostcheck.c    http-connect.o  http-server.o  openssl_hostname_validation.h  time-test
dns-example.o    hello-world        hostcheck.h    https-client.c  include.am     signal-test                    time-test.c
event-read-fifo  hello-world.c      http-connect   http-server     le-proxy.c     signal-test.c                  time-test.o
guoqi1@ubuntu:~/Desktop/libevent-2.1.11-stable/sample$ 

我们来简单的使用一下hello-world。当运行hello-world服务端之后,客户端链接之后就会成功的打印出我们耳熟能详的 hello world! 同时,服务端也会打印出 flushed answer以示回应。
在这里插入图片描述
在这里插入图片描述

2. libevent库的使用

2.1 没有带数据缓冲区的事件处理

当libevent库安装完成之后,句可以使用了,
使用套路:
1.创建一个事件处理框架
事件处理框架——event_base结构体的创建。
函数给我们分配了一块指向event_base类型的结构体的内存,内存当中封装的是poll,select,epoll,kequeue这些I/O接口,这个框架里面是没有任何具体的事件,需要接下event_new去创建一个事件,并添加进这个框架。

struct event_base* event_base_new(void);
/*函数给我们分配了一块指向event_base类型的结构体的内存,内存当中封装的是poll,select,epoll,kequeue这些I/O接口*/
    struct event_base* base = NULL;
    base = event_base_new();

2.创建一个事件
event_new 初始化一个新的事件结构体,事件中包含了事件类型,回调函数等,

//	short what
	~~#define  EV_TIMEOUT         0x01~~     // 废弃
	#define  EV_READ            0x02         //读事件
	#define  EV_WRITE           0x04		 //写时间
	#define  EV_SIGNAL          0x08    //libevent封装了信号相关的操作 SIGNAL
	#define  EV_PERSIST         0x10    // 持续触发模式
	#define  EV_ET              0x20    // 边沿模式模式

 // 创建新事件
	struct event *event_new(
		struct event_base *base, 
		evutil_socket_t fd, - // 文件描述符 - int 类型
		short what, //处理的事件类型,常用的包括读,写,信号操作等
		event_callback_fn cb, // 回调处理动作   _cb
		void *arg //回调函数传参
); 
// 事件的处理回调函数
	typedef void (*event_callback_fn)(evutil_socket_t, short, void *); 

3.事件添加到事件处理框架上
当事件被创建之后,还需要将其添加到event_base这个框架中去,否则不能对其进行操作。

// event_add
	int event_add(
				struct event *ev, //创建的事件
				const struct timeval *tv
				/*tv有如下传值:
				NULL:事件被触发,直接调用回调函数
				0-100:具体时间,阻塞该时间,待到这段时间过后,在调用回调函数,和select函数时间参数一样
				返回值:函数调用成功返回0,失败返回1 */
				); 

4.开始事件循环
事件添加框架之后,就可以开启事件循环,直到事件结束。一般不会使用退出函数,知道就ok。

// event_base_dispatch(简化版event_base_loop())
	int event_base_dispatch(struct event_base* base);
	//等同于没有设置标志的 event_base_loop ( )
	//将一直运行,直到没有已经注册的事件了

其他:需要关注的是事件循环的停止,如果需要设定循环的时间,或者立即退出循环有以下函数可以调用

//等待事件循环结束之后,立即退出。
	 int event_base_loopexit(
				struct event_base *base,
				const struct timeval *tv/*设定一定的时间之后退出,但是在设定的时间到了之后
				                 循环依旧在执行当前的事件,那么也会等待事件执行完毕之后退出。*/
				);
//参数struct timeval *tv
			struct timeval {
			       long   tv_sec;                    
				   long   tv_usec;            
			};

//立即退出循环,不管事件循环处理得什么样子。
		 int event_base_loopbreak(struct event_base *base);
		 
//返回值: 成功 0, 失败 -1

5.释放资源

// 释放事件
	void event_free(struct event *event); //释放事件
	 event_base_free(base);//释放框架

为了更加的直观理解整个事件的处理结构,整理了以下的核心伪代码:

#include <event2/event.h>
int main()
{
   //创建事件框架
    struct event_base* base = NULL;
    base = event_base_new();
    // 创建事件
    struct event* ev = NULL;
    ev = event_new(base, fd, EV_WRITE , write_cb, NULL);//初始化事件、回调函数
    // 添加事件
    event_add(ev, NULL);
    // 事件循环
    event_base_dispatch(base);
    // 释放资源
    event_free(ev);
    event_base_free(base);
    close(fd);
    return 0;
}
// 回调函数
void write_cb(evutil_socket_t fd, short what, void *arg)
{
    char buf[1024] = {0};
    write(fd, buf, strlen(buf)+1);
}

2.2 bufferevent 带缓冲区的事件

带缓存区的事件和以上事件的创建处理流程是一样的,但是带缓存区的事件主要还是用来处理读写操作,所以在读写缓冲区的回调函数操作中就多了一些新的API。以下将会以常用的Socket为例来详解libevent库的具体使用。
1.bufferevent-base 事件框架的创建,同上。
在这个结构体中的回调函数中,

struct event_base *base;
base = event_base_new();

2.bufferevent 事件的创建以及添加到事件框架中
对于服务端来说,对于最常使用的socket来说,libevent库中给我们封装了一系列的socket监听处理接口,首先创建evconnlistener *listener 监听事件,通过evconnlistener_new_bind()函数,完成了监听事件的初始化操作。其次在listener_cb()回调函数中,完成了bufferevent 事件的创建,并通过bufferevent_socket_new()函数完成了bufferevent 事件的初始化操作,主要包括事件的套接字绑定,并将事件添加到event_base框架中。最后当准备工作完成之后,读写操作就可以利用相对应的封装函数进行,bufferevent_write(),bufferevent_read() 函数。当然还可以利用 ufferevent_setcb()函数进行读写事件的反馈回调操作等。

struct evconnlistener *listener;
listener = evconnlistener_new_bind(base, listener_cb, (void *)base,
	    LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1,
	    (struct sockaddr*)&sin,
	    sizeof(sin));//listener监听事件的初始化
//listener_cb回调函数	    
static void listener_cb(struct evconnlistener *listener, evutil_socket_t fd,
    struct sockaddr *sa, int socklen, void *user_data)
{
	struct event_base *base = user_data;
	struct bufferevent *bev;//bufferevent事件的创建
	bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);//bufferevent事件的初始化
	if (!bev) {
		event_base_loopbreak(base);
		return;
	}
	bufferevent_setcb(bev, NULL, conn_writecb, conn_eventcb, NULL);//bufferevent事件的回调操作
	bufferevent_enable(bev, EV_WRITE);//写操作使能
	bufferevent_disable(bev, EV_READ);//关闭读操作
	bufferevent_write(bev, MESSAGE, strlen(MESSAGE));
}

3.事件的循环,资源的释放
这两步和上同样

	event_base_dispatch(base);//事件循环
	evconnlistener_free(listener);//释放监听事件,由于bufferevent事件封装在里面,一同释放
	event_base_free(base);//释放框架

4.相关函数的形参注解
为了加深对API的理解,在下面的完整伪代码中,分别对重要函数的内部参数进行了一定的注解,方便大家理解阅读。


#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
# include <arpa/inet.h>
#include <sys/socket.h>
#include <event2/bufferevent.h>//数据缓冲区的事件响应
#include <event2/buffer.h>
#include <event2/listener.h>
#include <event2/util.h>
#include <event2/event.h>
static const int PORT = 9999;
int main(int argc, char **argv)
{
	struct event_base *base;
	struct evconnlistener *listener;
	struct event *signal_event;
	struct sockaddr_in sin;
	base = event_base_new();
	
    memset(&sin, 0, sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(PORT);
	/*
	 evconnlistener_new_bind
	(
		struct event_base *base;
		evconnlistener_cb cd, //回调函数,接受连接之后,用户需要做的操作在回调函数里面
		void *ptr,//回调函数传参
		unsigned flags,
		int backlog,//-1 使用默认的最大值
		const struct sockaddr *sa,//服务器的IP和端口信息
		int socklen;
	)
	*/
	listener = evconnlistener_new_bind(base, listener_cb, (void *)base,
	    LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1,
	    (struct sockaddr*)&sin,
	    sizeof(sin));
		/*evconnlistener_new_bind();
		创建监听socket
		绑定
		监听
		等待并接受连接
		*/
	event_base_dispatch(base);//事件循环
	evconnlistener_free(listener);//释放sock资源
	event_base_free(base);
	return 0;
}

 void listener_cb(struct evconnlistener *listener, evutil_socket_t fd,
    struct sockaddr *sa, int socklen, void *user_data)
{
	struct event_base *base = user_data;
	struct bufferevent *bev;
		/*struct bufferevent_socket_new(
		   struct event_base *base,
		   evutil_socket_t fd,
		   enum bufferevent_options options
		); *///创建带缓冲区的事件
	bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
		/*
			void bufferevent_setcb(
			struct bufferevent *bufev,
			bufferevent_cb readcb,
			bufferevent_cb writecb,
			bufferevent_cb eventcb,
			void *cbarg);//给读写缓冲区设置回调函数
		*/
	bufferevent_setcb(bev, conn_readcb, conn_writecb, conn_eventcb, NULL);
	bufferevent_enable(bev, EV_WRITE);//写操作使能
	bufferevent_disable(bev, EV_READ);//关闭读操作
    char MESSAGE[] = "Hello, World\n";
	bufferevent_write(bev, MESSAGE, strlen(MESSAGE));
}
void conn_readcb(struct bufferevent *bev, void *user_data)
{
//****
		bufferevent_free(bev);
}
void conn_writecb(struct bufferevent *bev, void *user_data)
{
//***
		bufferevent_free(bev);
}
void conn_eventcb(struct bufferevent *bev, short events, void *user_data)
{
//***
	bufferevent_free(bev);
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值