详解libevent网络库(一)—框架的搭建

详解libevent网络库(一)—框架的搭建libevent 随手记 libevent 概述起源获取源码初识 libevent 框架学习 event base 重中之重从思想上出发一 拿出火箭壳 gt event base 创建与释放二 造螺丝 gt event new 创建与释放三 拧螺丝 gt event add 相关函数四 一节一节造火箭 gt event base dispatch 相关函数 l

libevent概述

Libevent 是一个用C语言编写的、轻量级的开源高性能事件通知库,主要有以下几个亮点:事件驱动( event-driven),高性能;轻量级,专注于网络,不如 ACE 那么臃肿庞大;源代码相当精炼、易读;跨平台,支持 Windows、 Linux、 *BSD 和 Mac Os;支持多种 I/O 多路复用技术, epoll、 poll、 dev/poll、 select 和 kqueue 等;支持 I/O,定时器和信号等事件;注册事件优先级。

起源

起初对libevent、libev、libuv等网络库有所耳目,未能拜读,最近在开发公司项目的时,发现底层组件之间交互采用了libevent进行编写,遂对其学习研究。公司中采用libevent 2.0之前的版本进行研发,对比发现,旧版本中很多函数过于冗杂,现对libevent 2.0之后的新版本进行学习研究:

在Libevent2.0之前的版本中,没有event_assign或者event_new函数,而只有event_set函数,该函数返回的event与“当前”base相关联。如果有多个event_base,则还需要调用event_base_set函数指明event与哪个base相关联

获取源码

Github: https://github.com/libevent/libevent

初识libevent

  1. 服务端:在上述路径执行 ./hello-world
  2. 客户端:采用nc进行访问 nc 127.0.0.1 9995 9995为上述代码中指定的端口号
    demo启动
    每有客户端注册时 server端将打印 flushed answer,同时,client端打印Hello, World!




框架学习– event_base 重中之重

夸张的理解为:libevent的世界中,event_base作为万物起源

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

从思想上出发

  • 我们可以把libevent项目想象为造火箭的过程,我们都只是螺丝工,那么造火箭需要做什么?

1. 拿出火箭壳(创建框架)
2. 造螺丝 (创建事件)
3. 拧螺丝 (添加事件)
4. 造火箭(事件循环)






一:掏出火箭壳 —>event_base()创建与释放

我们开始第一步:创建一个event_base

// 创建event_base struct event_base* event_base_new(void) 
// 释放event_base_free event_base_free(struct event_base* base); 

二:造螺丝 —>event_new()创建与释放

注:创建事件:(当前篇只针对不带缓冲区event事件进行讲解,有关缓存学习请见后续更新)

// 创建新事件 struct event *event_new( struct event_base *base, evutil_socket_t fd, - // 文件描述符 - int 底层是对epollin与epollout的封装 short what, event_callback_fn cb, // 事件的处理回调函数 void *arg //回调函数传参 ); // 事件的处理回调函数 typedef void (*event_callback_fn)(evutil_socket_t, short, void *); // 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 // 边沿模式 

在程序的最后我们需要 event_free 进行释放(但我们不得不提前考虑)

// 创建event_free void event_free(struct event *event); 

调用event_new()函数之后, 新事件处于已初始化非未决状态 (翻译:螺丝造好了,还没拧到火箭时的状态)

三:拧螺丝 —>event_add()相关函数

创建事件之后,在将其添加到 event_base 之前实际上是不能对其做任何操作的。
使用event_add()将事件添加到event_base。 (将螺丝拧上去)
非未决事件 -> 未决事件.




// event_add int event_add( struct event *ev, const struct timeval *tv ); 
– tv:参数 Value
NULL: 事件被触发, 对应的回调被调用
tv = {0, n} 设置的时间,在改时间段内检测的事件没被触发, 时间到达之后, 回调函数还是会被调用

函数调用成功返回0, 失败返回-1

设置非未决(作为了解实际中少用)
未决事件 -> 非未决事件.

//对已经初始化的事件调用 event_del()将使其成为非未决和非激活的。如果事件不是未决的或者激活的,调用将没有效果。 int event_del(struct event *ev); //成功时函数返回 0,失败时返回-1。 

四:一节一节造火箭 —>event_base_dispatch()相关函数

最后,我们只需将添加 事件循环
使用event_base_dispatch()函数

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

关于event_base_loop()函数

// event_base_loop() int event_base_loop(struct event_base *base, int flags); //正常退出返回0, 失败返回-1 //flages #define EVLOOP_ONCE 0x01 //事件只会被触发一次 //事件没有被触发, 阻塞等 #define EVLOOP_NONBLOCK 0x02 //非阻塞 等方式去做事件检测 //不关心事件是否被触发了 #define EVLOOP_NO_EXIT_ON_EMPTY 0x04 //没有事件的时候, 也不退出轮询检测 

关于退出事件循环函数event_base_loopexit()与event_base_loopbreak():

执行当前后退出 event_base_loopexit()

//如果 event_base 当前正在执行激活事件的回调 ,它将在执行完当前正在处理的事件后立即退出 int event_base_loopexit( struct event_base *base, const struct timeval *tv ); //参数struct timeval *tv struct timeval { 
    long tv_sec; long tv_usec; }; 

立即退出循环 event_base_loopbreak()

//让event_base 立即退出循环 int event_base_loopbreak(struct event_base *base); //返回值: 成功 0, 失败 -1 

Demo

//write_fifo.c #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <string.h> #include <fcntl.h> #include <event2/event.h> // 回调函数 void write_cb(evutil_socket_t fd, short what, void *arg) { 
    // write管道 char buf[1024] = { 
   0}; static int num = 666; sprintf(buf, "hello, world == %d\n", num); write(fd, buf, strlen(buf)+1); } int main(int argc, const char* argv[]) { 
    // open file int fd = open("myfifo", O_WRONLY | O_NONBLOCK); if(fd == -1) { 
    perror("open error"); exit(1); } // 写管道 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; } 
//read_fifo #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <string.h> #include <fcntl.h> #include <event2/event.h> // 读取回调函数  void read_cb(evutil_socket_t fd, short what, void *arg) { 
    // 读管道 char buf[1024] = { 
   0}; int len = read(fd, buf, sizeof(buf)); printf("data len = %d, buf = %s\n", len, buf); printf("read event: %s", what & EV_READ ? "Yes" : "No");//对what类型对判断 } int main(int argc, const char* argv[]) { 
    unlink("myfifo"); //创建有名管道 mkfifo("myfifo", 0664); // open file int fd = open("myfifo", O_RDONLY | O_NONBLOCK); if(fd == -1) { 
    perror("open error"); exit(1); } // 读管道 struct event_base* base = NULL; base = event_base_new(); // 创建事件 struct event* ev = NULL; ev = event_new(base, fd, EV_READ | EV_PERSIST, read_cb, NULL); // 添加事件 event_add(ev, NULL); // 事件循环 event_base_dispatch(base); // 释放资源 event_free(ev); event_base_free(base); close(fd); return 0; } 

./read ./write 依次执行即可

下一篇将讲述 带数据缓冲区 – Bufferevent …利用Bufferevent 实现网络通讯

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/207638.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 下午1:25
下一篇 2026年3月19日 下午1:25


相关推荐

  • Win10 Ubuntu16.04/Ubuntu18.04双系统完美安装「建议收藏」

    Win10 Ubuntu16.04/Ubuntu18.04双系统完美安装「建议收藏」按照网上博客的安装教程安装的Win10+Ubuntu16.04双系统安装了好几遍都不成功?启动Ubuntu左上一直有个光标在闪?如果你的电脑也是双硬盘(装Windows系统的固态硬盘+机械硬盘),在安装Win10+Ubuntu16.04双系统前一定要提前了解如下这些安装要点。首先非常非常感谢博客作者们分享的Win10+Ubuntu16.04双系统安装教程,其中一些博客对笔者双系统的安装非…

    2022年7月24日
    11
  • encrypt函数_crypt12

    encrypt函数_crypt12数据加密数据加密利用密码技术对信息进行加密,实现信息的隐蔽,从而起到保护信息安全的作用。它通过加密算法和加密秘钥将原来是明文的文件或数据进行处理,使其成文不可读的一段代码,也就是所谓的“密文”,来达到

    2022年8月6日
    10
  • 2025豆包大模型行业深度报告:AI投入持续加大,C端、B端两手齐抓

    2025豆包大模型行业深度报告:AI投入持续加大,C端、B端两手齐抓

    2026年3月12日
    1
  • 10分钟理解ES6箭头函数

    10分钟理解ES6箭头函数前言面试中 ES6 是一大考点 当被问到箭头函数时 我们都会说 箭头函数很好用 而且再也不用操心 this 的指向了 面试官 箭头函数是挺好用的 那有哪些不适合使用箭头函数的场景呢 箭头函数在大多数情况下 是很好用的 但是为什么在有些场景 使用箭头函数后会产生问题 是不是箭头函数还不够完善 又有哪些场景会发生问题 该如何解决呢 为了防止血案的产生 重新吧这一块拎出来整理巩固一下 概念 ES6 允许使用箭头 gt 定义函数 箭头函数提供了一种更加简洁的函数书写方式 箭头函数多用于匿名函数的定

    2026年3月18日
    3
  • Error:Execution failed for task ‘:app:validateSigningDebug’. > Keystore file D:\Android_keystore\deb

    Error:Execution failed for task ‘:app:validateSigningDebug’. > Keystore file D:\Android_keystore\deb

    2021年3月12日
    234
  • jmeter的正则表达式提取器_jmeter正则提取器的使用

    jmeter的正则表达式提取器_jmeter正则提取器的使用当我们的请求有这种类型的多种数据,我们要怎么获取到全部?首先,先在正则表示式提取器里面添加我们找到的左右边界然后写好正则表达式最后确定边界是唯一的然后我们运行一下,一下只就运行全部出来了…

    2025年8月26日
    11

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号