3.23-libevent

发布于:2025-03-25 ⋅ 阅读:(31) ⋅ 点赞:(0)

libevent

事件处理框架 - event_base

  • API 函数

    在 event_base 底层封装了 IO 多路转接函数, 并且可以对事件进行检测和处理的

// 头文件
#include <event2/event.h>
// 创建一个时间处理框架
struct event_base * event_base_new(void);
// 释放一个事件处理框架
// 参数: even_base_new() 函数的返回值
void event_base_free(struct event_base * base);


// 查看底层支持的 IO 转接模型
const char ** event_get_supported_methods(void);
// 当前使用的 IO 转接模型
const char * event_base_get_method(const struct event_base * base);

事件循环

// 头文件
#include <event2/event.h>
// 启动事件处理框架中的事件循环
// 参数: event_base_new() 返回值
/*
	事件检测过程中的特点:
		举例: 检测读事件:
			- 如果对方没有发送数据, 会一直检测直到触发
			- 加你测到有读事件, 默认只会处理一次, 事件循环就终止了, 只能接受一次数据
			- 要持续的接收数据, 需要额外设置 
*/
// 这个函数被调用, 内部会进行事件检测, 代码阻塞在这一行
// 条件满足, 检测就停止了, 应用程序就结束了
int intevent_base_dispath(struct event_base* base);
  • 终止事件循环
// 表示一个时间间隔
struct timeval
{
    long tv_sec;
    long tv_usec;
}
// 假设事件循环正在继续, 并且正在处理一个有效的事件, 调用这个函数不会马上终止, 在 tv 指定的时长之后, 事件就终止了
int event_base_loopexit(struct event_base * base, const struct timeval * tv)
参数:
	- base: 事件处理框架
    - tv: 在指定时间段之后, 退出事件循环
// 马上终止循环
int event_base_loopbreak(struct event_base * base);

事件

  • 事件的创建和释放

    // 事件处理函数, 被 libevent 框架调用, 实参的指定不是程序员做的
    // 程序员可以直接使用回调函数的参数 ==> 需要知道参数的意义
    typedef void (* event_callback_fn)(evutil_socket_t fd, short what, void * arg);
    参数:
    	- fd: 文件描述符,event_new() 第二个参数
        - what: 记录了实际文件描述符触发的事件
        - arg: event_new() 的最后一个参数
    
    
    // 本质是对一个文件描述符进行封装
    // 事件被创建之后, 不能被事件处理框架直接检测
    struct event_base * event_new(struct event_base * base, evutil_socker_t fd,
                        		short what, event_callback_fn cb, void * arg)
    参数:
    	- base: 事件处理框架
        - fd: 文件描述符, 比如管道的或者套接字的
        - what: 要检测的事件, 检测第二个参数 fd 的事件
            - EV_READ: 读事件
            - EV_WRITE: 写事件
            - EV_PERSIST: 设置事件重复检测
            - EV_SIGNAL: 信号事件(linux)
            - EV_ET: 设置边沿模式
        - cb: 回调函数, 当检测的事件被触发, 这个函数就被调用
        - arg: 作为实参传递给 cd
    
  • 事件的添加和删除

    struct timeval {}
    // 事件被创建之后, 不能被事件处理框架直接检测, 需要做添加处理
    int event_add(struct event * ev, const struct timeval * tv)
    参数:
    	- ev: 通过 event_new() 创建得到的事件
        - tv: 超时时长, 不用就传 NULL
            - 检测了一个时间, 并且在 tv 指定的时间段中没有被处罚, 这个事件对应的回调函数会被强制调用
            - 如果指定为 NULL, 事件不触发, 对应的回调函数就不被调用
    int event_del(struct event * ev);(不常用)
    参数:
    	- ev: 通过 event_new() 创建得到的事件
    

带缓冲区的事件

使用场景: 网络套接字通信

事件处理框架对 bufferevent 的处理方式:

  • 不需要调用函数将其添加到事件处理框架上, 没有对应的 add 函数
  • bufferevent 对应的事件在事件处理框架上默认是持续检测的
/*
带缓冲区的事件, 名字: bufferevent, 也是一个结构体
带缓冲区的事件结构:
	1. 封装了 socket 中的通信的文件描述符 -> int -> 得到了 bufferevent
	2. 和原来的区别:
		默认的通信的文件描述符:
			1. 在内核中对应两块内存, 读缓冲区, 写缓冲区
        struct bufferevent 对内核的两块内存进行了封装
        	1. 使用者不能直接操作内核中的读写缓冲区
        	2. 在 bufferevent 中又额外提供了两块缓冲区
        		- 读 -> 内核中的读缓冲区
        			- 自动将内核读缓冲区中的数据放到 bufferevent 的读缓冲区
        			- 使用者只需要从 bufferevent 读缓冲区中读数据即可
                - 写 -> 内核中的写缓冲区
                	- 要发送数据, 将数据写入 bufferevent 写缓冲区中即可
        	3. 读写缓冲区分别对应读写事件
        		- 当读事件被触发, 读缓冲区对应的回调函数被调用
        			- 在回调函数中读数据(接收数据)
                - 当写事件被触发, 写缓冲区对应的回调函数被调用
        			- 写缓冲区的数据被写到内核中了(没有什么卵用)
                
*/
  • 创建/释放基于套接字的 buffevent

    // 创建带缓冲区的事件, 起始对通信的套接字进行封装
    struct bufferevent * bufferevent_socket_new(
    	struct event_base * base,
    	evutil_socket_t fd,
    	enum bufferevent_options options
    )
    参数:
    	- base: 事件处理框架
        - fd: 通信的文件描述符
            - 自己创建通信的文件描述符, 通过函数: socket();
    		- 自动初始化: -1
        - opentions: 
    		- BUV_OPT_CLOSE_ON_FREE: 在销毁这个对象的时候, 自动释放封装的文件描述符
                
    // 释放带缓冲区的事件
    void bufferevent_free(struct bufferevent * bev);
    
  • 在 bufferevent 上启动连接

    // 连接服务器端的程序, 类似于 connect() 函数
    int bufferevent_socket_connect(
    	struct bufferevent * bev,
        struct sockaddr * address,
        int addrlen
    );
    参数:
    	- bev: 带缓冲区的事件, 通过 bufferevent_socket_new() 得到的
        - address: 需要在这里初始化服务器端的 IP 和端口信息
        - addlen: address 参数执行的内存大小
    
  • 操作 bufferevent 中的数据

    // 数据发送, 其实数据通过这个函数写入 bufferevent 的写缓冲区中
    int bufferevent_write(struct bufferevent * bufev, const void * data, size_t size);
    参数:
    	- bufev: bufferevent_socket_new() 得到的返回值, 封装了通信的文件描述符
        - data: 要发送的数据
        - size: data 的长度
    返回值:
    	- > 0: 成功
        - -1: 失败
    // 数据接收, 其实数据通过这个函数从 bufferevent 的读缓冲区中读出的
    int bufferevent_read(struct bufferevent * bufev, void * data, size_t size);
    参数:
    	- bufev: bufferevent_socket_new() 得到的返回值, 封装了通信的文件描述符
        - data: 存储数据的内存
        - size: data 的大小