Libevent

文章目录

      • libevent 简介
      • libevent 库安装及测试
      • 两种状态
      • libevent 框架
      • 相关函数
      • 查看 支持哪些 多路IO
      • 查看 当前用的 多路IO
      • 查看 fork后子进程使用的 event_base
      • 添加事件到 base 上
      • 将事件从 base上拿下
      • 释放事件
      • 带缓冲区的事件 bufferevent
      • bufferevent 创建、释放
      • 启用缓冲区
      • 客户端连接服务器
      • libevent实现 TCP 服务器实现流程
      • libevent实现 TCP 客户端实现流程
      • 简单使用

libevent 简介

libevent 库全部是基于 “事件” 的异步通信模型。

  • 专注:网络应用开发开源库。
  • 精简:相较于其他开源库,很小。
  • 开源:源代码开放。
  • 跨平台:windows、Linux、MacOs、unix …
  • 库下载路径:http://libevent.org

libevent 库安装及测试

  1. 加压缩 : tar zxvf libevent-2.1.8-stable.tar.gz
  2. 进入解压缩后的目录。cd
  3. 执行 ./configure 命令。—— 检测当前安装环境是否满足。生成 makefile 文 件
  4. 执行 make 命令。编译 libevent 库,生成对应文件。
  5. 执行 sudo make install 命令。安装 libevent 库(本质:将指定配置文件,拷 贝存放到指定目录)。
  1. 验证:
    1. cd 到 sample 目录,查看到 hello-world.c 文件
    2. 编译 hello-world.c 生成可执行文件。
    gcc hello-world.c -o hello -levent
    -L 库路径。 -I 头文件目录位置。
    3. ./hello 启动可执行程序
    4. 使用 nc 借助 9995 端口测试。 nc 127.1 9995 ——> 看到服务器回发的 “hello,world!” 数据。
    7. 生成的 libevent.so 动态库的存储目录位置为: /usr/local/lib

两种状态

未决和非未决态
- 未决:有资格被处理,但尚未被处理。
- 非未决:没有资格被处理。

Libevent_第1张图片

libevent 框架

  1. 创建 event_base
    #include
    struct event_base *event_base_new(void);
    // 例
    struct event_base *base = NULL;
    base = event_base_new();
  2. 常规事件 event —> event_new()
    带缓冲区事件 bufferevent —> bufferevent_socket_new();
  3. 启动循环监听
    int event_base_dispatch(struct event_base *base);
    // 相当于在 该函数内部,封装了 while(1) { … }
    但是,必须要借助 event_new() 中的 what 形参 指定了 EV_PERSIST 宏,才 while(1), 否则只执行一次。
    // 该函数以后的代码,必须要该函数执行结束(跳出循环)才有执行机会。
    // 例:
    event_base_dispatch(base);
  4. 释放 base
    void event_base_free(struct event_base *base);
    // 例:
    event_base_free(base);

相关函数

查看 支持哪些 多路IO

const char **event_get_supported_methods(void);

void test()
{
	//const char **event_get_supported_methods(void);

	char** res = event_get_supported_methods();
	for (int i = 0; i < 5; i++)
	{
		printf("%s\n", res[i]);
	}

}

查看 当前用的 多路IO

const char * event_base_get_method(const struct event_base *base);

void test()
{
	//const char * event_base_get_method(const struct event_base *base);
	struct event_base* base = event_base_new();

	const char* res = event_base_get_method(base);
	
	printf("%s\n", res);
}

查看 fork后子进程使用的 event_base

int event_reinit(struct event_base *base);
成功: 0, 失败: -1
使用该函数后, 父创建的base才能在子进程中生效。

struct event *event_new(struct event_base *base,evutil_socket_t fd,
							short what,event_callback_fn cb; void *arg);
	base: event_base_new() 函数返回值 —— “积木底座”
	fd:待监听的 文件描述符。
	what:待添加的 监听事件。
		EV_READ: 读事件
		EV_WRITE:  写事件
		EV_PERSIST: 持续触发。 结合 event_base_dispatch() 使用。用 “|” 连接
    cb:回调函数。
    arg:回调使用的 参数。
    返回值:成功创建的 常规事件 对象。
回调函数类型:

typedef void (*event_callback_fn)(evutil_socket_t fd, short what, void *arg)
// 例:
void read_cb(evutil_socket_t fd, what, arg)
{
    Read();
   		====数据处理====
    write();
    reutrn ;
}    

添加事件到 base 上

int event_add(struct event *ev, const struct timeval *tv);
	ev: event_new()函数返回的事件
    tv: 超时时间(秒、微秒)如果为NULL,不会超时。意为:一直等到事件被触发,回调函数会被调用。

将事件从 base上拿下

int event_del(struct event *ev); 
		ev:event_new() 函数返回的事件

释放事件

int event_free(struct event *ev);	
		成功: 0, 失败: -1

带缓冲区的事件 bufferevent

  • bufferevent 特性
  • 带有两个缓冲区的 event。一个读缓冲、一个写缓冲。

bufferevent 创建、释放

  • 创建
// 对应 event_new(), 但,没有指定 回调函数。
struct bufferevent *bufferevent_socket_new(struct event_base *base,                              evutil_socket_t fd, enum bufferevent_options options);
				
	base: event_base_new() 返回值。“积木底座”
	fd:待监听的 文件描述符。
	options:BEV_OPT_CLOSE_ON_FREE。关闭 socket时,释放 bufferevent
	返回值:成功创建一个 bufferevent 对象。
  • 释放
void bufferevent_free(struct bufferevent *bev);
 	bev: bufferevent_socket_new() 返回值。
  • bufferevent 设置回调
void bufferevent_setcb(struct bufferevent *bufev,
				bufferevent_data_cb readcb,
				bufferevent_data_cb writecb,
				bufferevent_event_cb eventcb,
				void *cbarg);
	bufev: bufferevent_socket_new() 返回值。
	readcb: 读回调 ——> 读缓冲
	writecb: 写回调 ——> 写缓冲,可以传NULL
	eventcb: 事件回调 ——> 通常传 NULL
	cbarg:上述 回调函数使用的参数
  • 读回调类型:
typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx);
	bev: bufferevent_socket_new() 返回值。
	ctx: bufferevent_setcb() 函数的 最后一个参数 cbarg
// 例:
void read_cb(struct bufferevent *bev, void *ctx)
{
        // 不能使用 read() 因为没有fd。 fd 被封装到 bev 对象内部。
        bufferevent_read();  
}
// 上述回调函数,在 bev 对象的读缓冲区中,有数据时,会被自动调用(回调)。

// bufferevent_read()函数原型。
size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
  • 写回调类型:
typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx) 
    bev: bufferevent_socket_new() 返回值。
	ctx: bufferevent_setcb() 函数的 最后一个参数 cbarg    
// 例:
先调用 bufferevent_write();  将数据写入到 bev 的 写缓冲中,写缓冲刷新之后,下述函数才会被回调。
void write_cb(struct bufferevent *bev, void *ctx) 
{
    // 打印,写操作完成!!!
}    
// bufferevent_write()函数原型。
size_t bufferevent_write(struct bufferevent *bufev, const void *data, size_t size);
  • 事件回调类型:
typedef void (*bufferevent_event_cb)(struct bufferevent *bev, short events, void *ctx);
	bev: bufferevent_socket_new() 返回值。
	events:客户端建立连接时,使用 BEV_EVENT_CONNECTED 传参。
	ctx: bufferevent_setcb() 函数的 最后一个参数 cbarg 
// 例:
void event_cb(struct bufferevent *bev, short events, void *ctx)
{
    // 事件满足时,回调的处理东西
}    

启用缓冲区

  • 默认创建的 bufferevent 对象具备r、w 两个缓冲区。

  • 写缓冲,默认是 enable的。 可以直接写入数据。

  • 读缓冲,默认是 disable的。不可以直接从中读数据。
    打开读缓冲的权限。使用 bufferevent_enable 函数

void bufferevent_enable(struct bufferevent *bufev, short events);
	bev: bufferevent_socket_new() 返回值。
	events: EV_READ、EV_WRITE、EV_READ|EV_WRITE
// 例:
bufferevent_enable(bufev, EV_READ);

其他操作:

  • 禁用缓冲区:

void bufferevent_disable(struct bufferevent *bufev, short events);

查看缓冲区的使能状态:

short bufferevent_get_enabled(struct bufferevent *bufev);

客户端连接服务器

int bufferevent_socket_connect(struct bufferevent *bev, struct sockaddr *address, 								int addrlen);
	bev: bufferevent_socket_new() 返回值。
	address: 服务器端的地址结构(IP+port)		(connect()函数的参2)
	addrlen: address的大小。				 (connect()函数的参3

创建监听客户端连接的监听器

// 创建服务器监听器 —— 监听客户端连接请求
struct evconnlistener *evconnlistener_new_bind(	
    struct event_base *base, evconnlistener_cb cb, void *ptr, unsigned flags,
    int backlog, const struct sockaddr *sa, int socklen);

	base:event_base_new() 函数返回值 —— “积木底座”
	cb:回调函数。
	ptr:回调函数参数。
	flags:标志位:
		LEV_OPT_CLOSE_ON_FREE: 结束时,释放 bufferevent 对象。
		LEV_OPT_REUSEABLE:设置 端口复用。 使用 “|” 设定两个值。
	backlog:listen()的第二个参数。 传-1 ---> 128	
	sa:服务器地址结构(IP+port)
    socklen: sa的大小。
    
	返回值:成功创建的监听器。

evconnlistener_cb 回调函数的类型

typedef void (*evconnlistener_cb)(struct evconnlistener *listener,  
			evutil_socket_t sock, struct sockaddr *addr, int len, void *ptr);
	listener:evconnlistener_new_bind() 函数返回值。
	sock: 通信套接字。cfd
	addr: 传出。客户端地址结构
	len: addr大小。
	ptr:evconnlistener_new_bind() 函数的 ptr 参数。
	
// 例:
void listen_cb(struct evconnlistener *listener,  
			evutil_socket_t sock, struct sockaddr *addr, int len, void *ptr)
{
  	// 该回调函数,当有客户端连接请求发生时,由系统自动调用并传参。  
}    
  • 释放监听器

void evconnlistener_free(struct evconnlistener *lev);

libevent实现 TCP 服务器实现流程

具体实现demo请看GitHub

  1. 创建 base 。 struct event_base *base = event_base_new();
  2. 创建、初始化服务器地址结构 sockaddr_in srv_addr;
  3. 创建 服务器监听器 listener。 调用 evconnlistener_new_bind()
  4. 设置回调 listener_cb() 实现。将 base 传参到回调函数内。
  5. 创建 bufferevent 对象。bev = bufferevent_socket_new(base)
  6. 给 bufferevent 设置回调。 bufferevent_setcb(bev, read_cb, write_cb, NULL)
  7. 实现 read_cb 回调函数。
    - bufferevent_read() 读客户端数据。
    - 打印读到的数据。(小 – 大)
    - bufferevnet_write() 写数据给客户端。
  8. 设置回调完成后,开启读缓冲区权限。bufferevent_enable(EV_READ)
  9. 启动监听 base。 event_dispatch(base);
  10. 释放事件。listener、base。

libevent实现 TCP 客户端实现流程

  1. 创建 event_base。 创建 socket() --- fd
    
  2. 使用 bev = bufferevnet_socket_new(fd) 创建一个用跟服务器通信的 bufferevnet 事件对象
    
  3. 使用 bufferevnet_socket_connect(bev) 连接 服务器
    
  4. 使用 bufferevent_setcb() 给 bufferevnet对象的 read、write、event 设置回调
    
  5. 设置 bufferevnet对象的读缓冲区 enable
    
  6. 在 read_cb 回调函数内,接收、发送数据 bufferevent_read() / bufferevent_write() 
    
  7. 启动循环监听 event_base_dispath(base) 
    
  8. 释放资源。
    

简单使用

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 


void sys_err(const char *str)
{
	perror(str);
	exit(1);
}

void read_cb(struct bufferevent *bev, void *ctx)
{
     char buf[4096] = {0}; 
       
     int ret = bufferevent_read(bev, buf, sizeof(buf));  
     printf("sever read: %s\n", buf); 
     
     char *p = "this is a test for bufferevent server\n";
     bufferevent_write(bev, p, strlen(p)+1);
}

void write_cb(struct bufferevent *bev, void *ctx)
{
     printf(" ---- bufferevent_write finish!!!\n");    
}

void listen_cb(struct evconnlistener *listener,  
			evutil_socket_t cfd, struct sockaddr *addr, int len, void *ptr)
{
	struct event_base *base = (struct event_base *)ptr;
	
  	struct bufferevent *bev = NULL;
  	
  	bev = bufferevent_socket_new(base, cfd, BEV_OPT_CLOSE_ON_FREE); 
  	
  	bufferevent_setcb(bev, read_cb, write_cb, NULL, NULL);
  	
  	bufferevent_enable(bev, EV_READ);
} 
 			 
int main(int argc, char *argv[])
{
	struct event_base *base = NULL;
	base = event_base_new();
	
	struct sockaddr_in srv_addr;
	
	bzero(&srv_addr, sizeof(srv_addr));
	srv_addr.sin_family = AF_INET;
	srv_addr.sin_port = htons(9999);
	
	struct evconnlistener * listener = NULL;
	listener = evconnlistener_new_bind(base, listen_cb, base, LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE,
						 -1, (struct sockaddr *)&srv_addr, sizeof(srv_addr));
						 
	event_base_dispatch(base);
		
	evconnlistener_free(listener);				 
	event_base_free(base);

	return 0;
}

更多参考https://dengjin.blog.csdn.net/article/details/90758999

你可能感兴趣的:(Linux网络编程,libevent)