【Libevent】Libevent特征和事件集创建

1.libevent特点

  • 事件驱动,高性能
  • 轻量级,专注于网络;
  • 跨平台,支持Windows、Linux、Linux、Mac Os等
  • 支持多种I/O多路复用技术,epoll、poll、dev/poll、select、kqueue等
  • 支持I/O,定时器和信号等事件

2.event_base

使用 libevent 函数之前需要分配一个或者多个 event_base 结构体。每个 event_base 结构 体持有一个事件集合,可以检测以确定哪个事件是激活的。

如果设置 event_base 使用锁,则可以安全地在多个线程中访问它 。然而,其事件循环只能 运行在一个线程中。如果需要用多个线程检测 IO,则需要为每个线程使用一个 event_base。

每个 event_base 都有一种用于检测哪种事件已经就绪的 “方法”,或者说后端。可以识别的方法有:

select、poll、epoll、kqueue、devpoll、evport、win32

2.1创建默认的event_base

***event_base_new()***函数分配并且返回一个新的具有默认设置的 event_base。函数会检测环境变量,返回一个到 event_base 的指针。如果发生错误,则返回 NULL。

struct event_base *event_base_new(void);
2.2创建复杂的event_base

要对event_base有更多的控制,需要使用event_config函数。event_config 是一个容纳 event_base 配置信息的不透明结构体。需要 event_base 时,将 event_config 传递给event_base_new_with_config ()

创建接口

分配一个event_config结构体
struct event_config *event_config_new(void);

获取新的event_base
struct event_base* event_base_new_with_config(const struct event_config *cfg);

释放event_config
void event_config_free(struct event_config *cfg);

使用步骤

  • 要使用这些函数分配event_base,先调用 event_config_new()分配一个 event_config。
  • 然后,对 event_config 调用其它函数,设置所需要的 event_base 特征。
  • 最后,调用 event_base_new_with_config()获取新的 event_base。
  • 完成工作后,使用 event_config_free ()释放 event_config。
2.3设置event_ base特征

vent_config_avoid_method()

调用 event_config_avoid_method ()可以通过名字让 libevent 避免使用特定的可用后端 。

int event_config_avoid_method(struct event_config *cfg, const char *method);

event_config_require_features()

调用 event_config_require_feature ()让 libevent 不使用不能提供所有指定特征的后端,底层也是一个位图

int event_config_require_features(struct event_config *cfg,enum event_method_feature feature);

event_config_require_features()可识别的特征值有:

  • EV_FEATURE_ET:要求支持边沿触发的后端
  • EV_FEATURE_O1:要求添加、删除单个事件,或者确定哪个事件激活的操作是 O(1)复杂度的后端
  • EV_FEATURE_FDS:要求支持任意文件描述符,而不仅仅是套接字的后端

event_config_set_flag()

让libevent 在创建 event_base 时设置一个或者多个将在下面介绍的运行时标志。

int event_config_set_flag(struct event_config *cfg,enum event_base_config_flag flag);


常用的特征属性,底层是一个位图
enum event_base_config_flag {
    EVENT_BASE_FLAG_NOLOCK = 0x01,
    EVENT_BASE_FLAG_IGNORE_ENV = 0x02,
    EVENT_BASE_FLAG_STARTUP_IOCP = 0x04,
    EVENT_BASE_FLAG_NO_CACHE_TIME = 0x08,
    EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST = 0x10,
    EVENT_BASE_FLAG_PRECISE_TIMER = 0x20
};    

event_config_set_flag()可识别的选项值有:

  • EVENT_BASE_FLAG_NOLOCK :不要为 event_base 分配锁。设置这个选项可以为 event_base节省一点用于锁定和解锁的时间,但是让在多个线程中访问event_base成为不安全的。
  • EVENT_BASE_FLAG_IGNORE_ENV :选择使用的后端时,不要检测 EVENT_* 环境 变量。使用这个标志需要三思:这会让用户更难调试你的程序与 libevent 的交互。
  • EVENT_BASE_FLAG_STARTUP_IOCP:仅用于 Windows,让 libevent 在启动时就 启用任何必需的 IOCP 分发逻辑,而不是按需启用。
  • EVENT_BASE_FLAG_NO_CACHE_TIME :不是在事件循环每次准备执行超时回调时 检测当前时间,而是在每次超时回调后进行检测。注意:这会消耗更多的 CPU时间。
  • EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST :告诉 libevent ,如果决定使 用 epoll 后端,可以安全地使用更快的基于 changelist 的后端。epoll-changelist后端可以在后端的分发函数调用之间,同样的 fd 多次修改其状态的情况下,避免不必要的系统调用。但是如果传递任何使用 dup()或者其变体克隆的 fd 给 libevent,epoll-changelist 后端会触发一个内核 bug,导致不正确的结果。在不使用 epoll 后端的情况下,这个标 志是没有效果的。
  • 也可以通过设置 EVENT_EPOLL_USE_CHANGELIST 环境变量来 打开 epoll-changelist 选项。
2.4检测event_base后端
2.4.1接口一

有时候要检查 event_base 支持哪些特征,或者当前使用哪种方法。

const char **event_get_supported_methods(void);

event_get_supported_methods()函数返回一个指针 ,指向 libevent 支持的方法名字数组。这个数组的最后一个元素是 NULL。

实例

int i;
const char **methods = event_get_supported_methods();
for (i=0; methods[i] != NULL; ++i) {
    printf("    %s\n", methods[i]);
}
2.4.2接口二
const char *event_base_get_method(const struct event_base *base);

enum event_method_feature event_base_get_features(const struct event_base *base);
  • event_base_get_method()返回event_base正在使用的方法。
  • event_base_get_features()返回event_base支持的特征的比特掩码。

实例

struct event_base *base;
enum event_method_feature f;

base = event_base_new();
if (!base) 
{
    puts("Couldn't get an event_base!");
} 
else {
    printf("Using Libevent with backend method %s.",event_base_get_method(base));
    f = event_base_get_features(base);
    if ((f & EV_FEATURE_ET))
        printf("  Edge-triggered events are supported.");
    if ((f & EV_FEATURE_O1))
        printf("  O(1) event notification is supported.");
    if ((f & EV_FEATURE_FDS))
        printf("  All FD types are supported.");
    puts("");
}
2.5释放event_base

使用完 event_base 之后,使用 event_base_free()进行释放。

void event_base_free(struct event_base *base);
2.6event_base优先级

libevent支持为事件设置多个优先级。然而, event_base默认只支持单个优先级。可以调用 event_base_priority_init()设置 event_base 的优先级数目。

int event_base_priority_init(struct event_base *base, int n_priorities);

成功时这个函数返回 0,失败时返回 -1。base 是要修改的 event_base,n_priorities 是要支持的优先级数目,这个数目至少是 1 。每个新的事件可用的优先级将从 0 (最高) 到 n_priorities-1(最低)。

2.7event_base和fork

不是所有事件后端都在调用fork()之后可以正确工作。比如在fork()前的地方创建,会发生写时拷贝。

所以,如果在使用fork()或者其他相关系统调用启动新进程之后,希望在新进程中继续使用 event_base,就需要进行重新初始化。

int event_reinit(struct event_base *base);

成功时这个函数返回 0,失败时返回 -1。

实例

struct event_base *base = event_base_new();
/* ... add some events to the event_base ... */
if (fork()) {
    /* In parent */
    continue_running_parent(base); /*...*/
} else {
    /* In child */
    event_reinit(base);		//在子进程中注册event_base事件
    continue_running_child(base); /*...*/
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
libevent是一个事件驱动的网络库,可以用来创建高效的网络应用程序。下面是使用libevent创建TCP客户端的步骤: 1. 引入libevent库头文件: ``` #include <event2/event.h> #include <event2/bufferevent.h> #include <event2/buffer.h> #include <event2/util.h> ``` 2. 创建event_base对象: ``` struct event_base* base = event_base_new(); ``` 3. 创建连接: ``` struct bufferevent* bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); bufferevent_setcb(bev, readcb, NULL, eventcb, NULL); bufferevent_enable(bev, EV_READ|EV_WRITE); bufferevent_socket_connect(bev, (struct sockaddr*)&serveraddr, sizeof(serveraddr)); ``` 其中,readcb是读取数据回调函数,eventcb是事件回调函数,serveraddr是服务器地址。 4. 发送数据: ``` const char* data = "hello, world!"; bufferevent_write(bev, data, strlen(data)); ``` 5. 接收数据: ``` void readcb(struct bufferevent* bev, void* ctx) { struct evbuffer* input = bufferevent_get_input(bev); size_t len = evbuffer_get_length(input); char* data = malloc(len+1); evbuffer_remove(input, data, len); data[len] = '\0'; printf("received data: %s\n", data); free(data); } ``` 6. 事件循环: ``` event_base_dispatch(base); ``` 完整代码示例: ``` #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <event2/event.h> #include <event2/bufferevent.h> #include <event2/buffer.h> #include <event2/util.h> void readcb(struct bufferevent* bev, void* ctx) { struct evbuffer* input = bufferevent_get_input(bev); size_t len = evbuffer_get_length(input); char* data = malloc(len+1); evbuffer_remove(input, data, len); data[len] = '\0'; printf("received data: %s\n", data); free(data); } void eventcb(struct bufferevent* bev, short events, void* ctx) { if (events & BEV_EVENT_CONNECTED) { printf("connected\n"); } else if (events & BEV_EVENT_ERROR) { printf("error\n"); } } int main(int argc, char** argv) { struct event_base* base = event_base_new(); struct sockaddr_in serveraddr; memset(&serveraddr, 0, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_port = htons(12345); inet_pton(AF_INET, "127.0.0.1", &serveraddr.sin_addr); struct bufferevent* bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); bufferevent_setcb(bev, readcb, NULL, eventcb, NULL); bufferevent_enable(bev, EV_READ|EV_WRITE); bufferevent_socket_connect(bev, (struct sockaddr*)&serveraddr, sizeof(serveraddr)); const char* data = "hello, world!"; bufferevent_write(bev, data, strlen(data)); event_base_dispatch(base); bufferevent_free(bev); event_base_free(base); return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

影中人lx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值