嵌入式开发必备:开源事件驱动库 libevent

上一篇我们分享了:libevhtp——一款专为嵌入式系统设计的开源HTTP库!,libevhtp依赖于libevent。这篇文章我们一起来了解libevent。

一、libevent 简介

libevent 是一个开源的、轻量级的跨平台事件驱动库:

Github仓库地址:https://github.com/libevent/libevent

libevent 的源码结构清晰,主要分为以下几个核心模块:

libevent/├── event.c                
# 事件核心逻辑├── epoll.c               
# Linux epoll 后端实现├── kqueue.c              
# BSD kqueue 后端实现├── select.c              
# 通用 select 后端实现├── bufferevent.c         
# 缓冲区事件处理├── evhttp.c              
# HTTP 协议处理├── evdns.c               
# DNS 解析模块├── evthread.c            
# 线程支持├── evutil.c              
# 工具函数

它封装了多种操作系统的 I/O 多路复用机制(如 epoll、kqueue、select 等),提供了统一的事件处理接口,极大简化了异步网络编程的复杂度。

1. 核心特性

  • 跨平台支持:原生支持 Linux、Windows、BSD 等多种操作系统,甚至在嵌入式系统上也能运行
  • 高性能 I/O 模型:自动适配底层最优的 I/O 多路复用机制(epoll/kqueue 优先)
  • 轻量级设计:核心代码简洁高效,资源占用低,非常适合嵌入式场景
  • 丰富的功能组件:包含 HTTP 服务器、DNS 解析、SSL 支持等实用模块
  • 线程安全:提供完善的线程安全机制,支持多线程环境下的事件处理

2. 优缺点分析

二、libevent实战

1. 源码获取与编译

# 克隆源码仓库
git clone https://github.com/libevent/libevent.git
cd libevent

# 创建编译目录
mkdir build && cd build

# 使用 CMake 配置(嵌入式平台需指定交叉编译工具链)
cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local/libevent

# 编译
make -j16

# 安装到指定目录
sudo make install

2. 简单的 TCP 服务器

下面是一个基于 libevent 的简单 TCP 服务器示例,实现了接收客户端连接并回显数据的功能:

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


/* 客户端连接处理回调函数 */
void client_read_cb(struct bufferevent *bev, void *ctx) {
    char buffer[1024];
    int len;
    
    /* 从缓冲区读取数据 */
    len = bufferevent_read(bev, buffer, sizeof(buffer) - 1);
    if (len <= 0) {
        printf("客户端连接关闭\n");
        bufferevent_free(bev);
        return;
    }
    
    buffer[len] = '\0';
    printf("收到客户端数据: %s", buffer);
    
    /* 将数据回显给客户端 */
    bufferevent_write(bev, buffer, len);
}

/* 客户端连接错误处理回调 */
void client_event_cb(struct bufferevent *bev, short events, void *ctx) {
    if (events & BEV_EVENT_EOF) {
        printf("客户端断开连接\n");
    } elseif (events & BEV_EVENT_ERROR) {
        printf("客户端发生错误\n");
    } elseif (events & BEV_EVENT_CONNECTED) {
        printf("客户端连接成功\n");
    }
    bufferevent_free(bev);
}

/* 新客户端连接处理回调 */
void accept_conn_cb(struct evconnlistener *listener, evutil_socket_t sock,
                    struct sockaddr *addr, int len, void *ctx) {
    struct event_base *base = evconnlistener_get_base(listener);
    struct sockaddr_in *sin = (struct sockaddr_in *)addr;
    char client_ip[INET_ADDRSTRLEN];
    
    /* 转换客户端IP地址格式 */
    inet_ntop(AF_INET, &(sin->sin_addr), client_ip, INET_ADDRSTRLEN);
    printf("新客户端连接: %s:%d\n", client_ip, ntohs(sin->sin_port));
    
    /* 创建缓冲区事件 */
    struct bufferevent *bev = bufferevent_socket_new(base, sock, BEV_OPT_CLOSE_ON_FREE);
    if (!bev) {
        printf("创建缓冲区事件失败\n");
        return;
    }
    
    /* 设置回调函数 */
    bufferevent_setcb(bev, client_read_cb, NULL, client_event_cb, NULL);
    
    /* 启用读写事件 */
    bufferevent_enable(bev, EV_READ | EV_WRITE);
}

/* 错误处理回调 */
void accept_error_cb(struct evconnlistener *listener, void *ctx) {
    struct event_base *base = evconnlistener_get_base(listener);
    int err = EVUTIL_SOCKET_ERROR();
    printf("监听错误: %s\n", evutil_socket_error_to_string(err));
    
    event_base_loopexit(base, NULL);
}

int main(int argc, char **argv) {
    struct event_base *base;
    struct evconnlistener *listener;
    struct sockaddr_in sin;
    int port = 9999;
    
    /* 初始化事件基 */
    base = event_base_new();
    if (!base) {
        printf("创建事件基失败\n");
        return1;
    }
    
    /* 配置监听地址 */
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = htonl(INADDR_ANY);
    sin.sin_port = htons(port);
    
    /* 创建监听套接字 */
    listener = evconnlistener_new_bind(base, accept_conn_cb, NULL,
                                      LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE,
                                      -1, (struct sockaddr *)&sin, sizeof(sin));
    if (!listener) {
        printf("创建监听失败\n");
        event_base_free(base);
        return1;
    }
    
    /* 设置监听错误回调 */
    evconnlistener_set_error_cb(listener, accept_error_cb);
    
    printf("服务器启动,监听端口: %d\n", port);
    
    /* 进入事件循环 */
    event_base_dispatch(base);
    
    /* 清理资源 */
    evconnlistener_free(listener);
    event_base_free(base);
    
    return0;
}

编译命令:

gcc -o tcp_server tcp_server.c -I/path/to/libevent/include -L/path/to/libevent/lib -levent -Wl,-rpath=/path/to/libevent/lib

Telnet协议是TCP/IP协议族中的一员,是Internet远程登陆服务的标准协议和主要方式。telnet命令可以用来确定远程服务的状态,比如确定远程服务器的某个端口是否能访问。

重要部分:

  • 事件基 (event_base):整个事件处理的核心,管理事件循环和事件分发
  • 连接监听器 (evconnlistener):封装了 TCP 服务器的监听逻辑,简化连接处理
  • 缓冲区事件 (bufferevent):提供了带缓冲区的事件处理,自动管理数据读写
  • 回调函数机制:libevent 采用异步回调模式,避免阻塞式编程
声明:本内容为作者独立观点,不代表电子星球立场。未经允许不得转载。授权事宜与稿件投诉,请联系:editor@netbroad.com
觉得内容不错的朋友,别忘了一键三连哦!
赞 3
收藏 3
关注 45
成为作者 赚取收益
全部留言
0/200
成为第一个和作者交流的人吧