libevent的使用

8-1 安装

​ 自己百度一下,安装它不是特别难,加油!!!

8-2 libevent介绍

​ 它是一个开源库,用于处理网络和定时器等等事件。它提供了跨平台的API,能够在不同的操作系统上实现高性能,可扩展的世界去的编程。

​ 1.事件驱动:libevent使用事件驱动模型,通过监听事件的就绪状态来触发相应的回调函数。(事件:网络I/O,信号,定时器等等)。

​ 2.跨平台:libevent可以在Linux,Unix,Windows等系统上使用。

​ 3.高性能:libevent底层采用epoll等等实现。并且采用非阻塞I/O技术,实现高效的事件处理。可以处理大量的并发连接和高负载情况。

​ 4.线程安全:libevent提供了安全的api,可以在多线程下使用。不同线程共享一个event_base对象,并且独立地注册和处理事件,保证线程之间的数据安全。

​ 5.定时器支持:libevent提供了定时器功能,可以注册和管理不同的定时器。程序员可以根据指定的定时器的触发时间来实现定时任务的调度和执行。

​ 6.异步DNS解析:可以在不阻塞主程序的情况下进行域名查询操作。

​ 7.可扩展性:允许程序员编写自定义的事件处理机制和回调函数。

​ 总之它很厉害。

8-3 libevent库的使用

​ 首先在程序中包含头文件 #include <event2/event.h>

​ 之后在使用gcc编译的时候要指定一个库 -levent 例如:gcc -o main main.c -levent

8-4 libevent的地基-event_base

​ 在使用libevent前,需要分配一个或者多个event_base结构体,每个结构体都有一个事件集合,可以检测那个事件是激活的。event_base结构体相当于epoll的红黑树根节点,每个结构体都有一种用于检测某种事件已经就绪的方法。

8-4-1创建结构体event_base函数

函数原型:struct event_base *event_base_new(void);

函数功能:创建一个event_base对象,用于管理事件循环,事件注册和分发等。

函数参数:无。

函数返回值

​ 成功:返回一个event_base类型的结构体指针。

​ 失败:返回NILL。

​ 例子:struct event_base *base = event_base_new(); //创建一个结构体,并且指针base指向那个结构体。

8-4-2释放结构体event_base_free函数

函数原型:void event_base_free(struct event_base * base);

函数功能:释放一个event_base对象,创建完event_base对象后,如果不用了记得释放掉。

函数参数:一个指向event_base对象的结构体指针。

函数返回值:无。

8-4-3重置结构体event_base函数

函数原型:int event_reinit(struct event_base *base);

函数功能:重置原来的event_base对象

函数参数:一个指向event_base对象的结构体指针。

函数返回值

​ 成功:返回0。

​ 失败:返回-1。

8-5查看libevent支持那些I/O复用

8-5-1获取当前平台支持的I/O复用方式函数

函数原型:const char **event_get_supported_methods(void);

函数功能:获取当前平台支持的I/O复用方法。

函数参数:无。

函数返回值:返回一个二维数组,将其打印即可得到支持的复用方式。

8-5-2获取当前event_base对象使用的I/O复用方式

函数原型:const char *event_base_get_method(const struct event_base *base);

函数功能:获取当前event_base对象使用的I/O复用方法。

函数参数:一个指向event_base对象的结构体指针。

函数返回值:返回一个字符串指针,打印即可获得想要的结果。

​ 注意:打印字符串的判断字符串结尾的条件是:字符串[i] != NULL

8-6循环等待event_loop(等待事件产生)

​ 在创建libevent的基础之后就需要等待事件的产生,所以程序就不能退出了,类似于之前的while(1)无限循环中不断监听文件描述符的某缓冲区是否有动静。于是libevent中也给出了类似的api接口,可以让我们直接进入循环中然后等待事件激活。

8-6-1事件循环函数event_base_loop函数

函数原型:int event_base_loop(struct event_base *base, int flags);

函数功能:在指定的event_base上运行事件循环,不断检测活动的事件,并且调用相应的回调函数。

函数参数

event_base:一个指向event_base对象的指针,指明要在那个对象上面运行事件循环。

flage:事件循环的标志位,用于指定事件循环的行为。

EVLOOP_ONCE:仅运行一次事件循环,处理完当前已经就绪的事情后立刻返回,没有事件则阻 塞等待。

EVLOOP_NONBLOCK:非阻塞模式运行事件循环,即使没有任何事件就绪也会立刻返回。

函数返回值

​ 成功:表示事件循环正常结束,返回0。

​ 失败:返回-1。

8-6-2事件循环函数event_base_dispatch函数

函数原型:int event_base_dispatch(struct event_base *base);

函数功能:在指定的对象上运行事件循环,不断检测活动的事件,并且调用回调函数。该函数默认以阻塞 模式运行。而且是一直循环检测,直到开发者调用相应的api函数,这个循环才会关闭,函数才会返回。

函数参数:一个指向event_base的指针。

函数返回值

​ 成功:循环正常结束返回0。

​ 失败:返回-1。

8-6-3结束事件循环event_base_loopexit函数(定时结束)

函数原型:int event_base_loopexit(struct event_base *base, const struct timeval *tv);

函数功能:在指定的时间过后立刻退出事件等待循环。

函数参数

event_base:一个指向event_base对象的指针,用于指定要结束那个对象的事件循环。

tv:一个timeval结构体的指针,用于指定等待时间。如果填NULL则立刻退出循环。

结构体timeval的成员

long tv_sec; //秒数

long tv_usec; //微妙

函数返回值:成功返回0。 失败返回-1。

8-6-4结束事件循环event_base_loopbreak函数(立刻结束)

函数原型int event_base_loopbreak(struct event_base);

函数功能:立刻退出事件等待循环。

函数参数

event_base:一个指向event_base对象的指针,用于指定要结束那个对象的事件循环。

函数返回值:成功返回0 。失败返回-1 。

8-7事件驱动event

8-7-1 event的几种状态

​ 1.无效指针:只是定义了一个event指针struct event *ptr,但未给他赋值,此时指针为无效状态。

​ 2.非未决:当创建了一个event对象,并且调用event_new函数为其分配了内存和初始化相关参数,但没有将其加入到事件循环中监听时,event对象就是非未决状态。

​ 3.未决:当通过event_add函数将event对象注册到event_base对象上进行监听时,event对象处于未决状态。此时event对象将于特定的世界源关联,等待事件的触发。

​ 4.激活:当已注册的事件开始发生,event会进入激活状态。此时libevent会自动调用与该对象关联的回调函数来处理该事件。

8-7-2 自定义回调函数

函数原型:typedef void(*event_callback_fn)(evutil_socket_t fd, short events, void *arg);

函数功能:定义了一个函数指针类型event_callback_fn,它指向一个回调函数,此回调函数在特定事件发生时被调用。

函数参数

evutil_socket_t fd:表示事件关联的文件描述符。

short events:表示事件的类型。(读事件,写事件)

void *arg:一个指向用户自定义数据的指针。

函数返回值:无。

8-7-3 创建事件对象 event_new函数

函数原型:struct event *event_new(struct event_base *base, evutil_socket_t fd, short events, event_callback_fn cb, void *arg);

函数功能:用于创建一个event对象,并且将event对象注册到event_base对象中去参与事件循环。并且返回该event对象的指针。

函数参数

struct event_base *base:指向event_base对象的指针。

evutil_socket_t fd:要关联的文件描述符。如果该描述符上发生了事件时,将会触发回调函数。

short events:表示注册的事件类型,例如读事件,写事件,定时器等等。

event_callback_fn cb:事件发生时要调用的回调函数。该回调函数指针遵循event_callback_fn函数指针类 型的定义。

void *arg:传递给回调函数的用户数据指针。

函数返回值:成功则返回一个event对象。失败返回NULL。

8-7-4 常用事件events

EV_TIMEOUT:超时时间。当指定的时间间隔到达时,触发相应的回调函数。

EV_READ:读事件。当一个文件描述符可读时,触发相应的回调函数。

EV_WRITE:写事件。当一个文件描述符可写时,触发相应的回调函数。

EV_SIGNAL:信号事件。当指定的信号发生时,触发相应的回调函数。

​ EV_PERSIST:周期性触发。当回调函数被触发后,事件任然保持注册状态,可以多次触发。

EV_ET:边缘触发(需要底层模型支持才可以生效)在此模式下,只有文件描述符状态发送变化时才会触发事件。

​ 注意:如果想要设置持续的读写事件可以这样:EV_READ | EV_PERSIST

8-7-5 使用宏定义快速创建信号事件对象

宏定义:#define evsignal_new(b, x, cb, arg) event_bew((b), (x), 事件, (cb), (arg))

宏参数

(b):指向基础event_base对象的指针。

(x):信号值,表示要监听的信号。

(cb):事件发生时的回调函数。填一个函数指针类型。

(arg):传递给回调函数的用户自定义数据指针。

8-7-6 将事件添加到事件循环的event_add函数

函数原型:int event_add(struct event *ev, const struct timeval *timeout);

函数功能:将事件添加到事件循环中。

函数参数

ev:指向要添加的事件的指针,并且该事件必须已经被初始化。

timeout:指向结构体struct timeval的指针,表示超时时间,如果填NULL则表示没时间限制。

函数返回值:成功返回0,失败返回-1。

8-7-7 将事件从事件循环中移除的event_del函数

函数原型:int event_del(struct event *ev);

函数功能:从事件循环中移除一个已经添加的事件。

函数参数:ev是指向要删除的事件的指针。

​ 函数返回值:成功则返回0,失败则返回-1。

8-7-8 释放event_new函数创建的event对象所占的内存资源event_free函数

函数原型:void event_free(struct event *ew);

函数功能:释放event_new函数创建的event对象所占的内存资源。

函数参数:ev是指向要释放的event对象。

函数返回值:无。

8-8使用libevent创建服务器的步骤

第一步:创建套接字,并且绑定本地地址,然后监听这个套接字描述符。

第二步:调用event_base_new函数创建一个event_base对象。

第三步:创建event事件,并且设置好相应的事件和回调函数,如果在回调函数中也要访问当前的event_base对象,那么就需要将该对象作为回调函数的参数传入进去。

第四步:调用event_add将该event事件对象加入到event_base对象中(上树)。

第五步:调用event_base_dispatch进入事件循环等待事件的发生。事件发生会自动调用相应的回调函数的。

第六步:如果服务端完成了工作则需要调用event_base_free释放刚才的event_base对象。调用event_free释放刚才的event事件对象。最后关闭套接字描述符。

8-9 服务端代码实例:

点击查看代码

  1. #include <event2/event.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <stdlib.h>
  5. struct event* connev = NULL;//全局变量事件
  6. //回调函数
  7. void fun(evutil_socket_t fd, short events, void* arg) {
  8. int n;
  9. char data[128];//接收来自客户端的数据
  10. char serv_data[256] = "服务器已经收到你的来信!";
  11. //接收并且处理数据
  12. n = recv(fd, data, sizeof(data), 0);
  13. if (n < 0) {
  14. printf("接收数据失败!\n");
  15. //记得删除事件
  16. event_del(connev);
  17. close(fd);
  18. }
  19. else {
  20. if (send(fd, serv_data, sizeof(serv_data), 0) == -1) {
  21. printf("数据发送失败\n");
  22. event_del(connev);
  23. close(fd);
  24. }
  25. }
  26. }
  27. //回调函数
  28. void func(evutil_socket_t fd, short events, void* arg) {
  29. //调用func时传入了地基base,然后需要我们将其赋值给func中的base,以便访问
  30. struct event_base* base = (struct event_base*)arg;
  31. //接收新的客户端连接
  32. int cfd = accept(fd, NULL, NULL);
  33. if (cfd < 0) {
  34. printf("建立连接失败!\n");
  35. return;
  36. }
  37. //新建事件,还是以base为地基,然后持续监听与客户端连接的cfd的读缓冲区
  38. //如果有动静则调用func
  39. connev = event_new(base, cfd, EV_READ | EV_PERSIST, fun, NULL);
  40. if (connev == NULL) {
  41. printf("error\n");
  42. //退出事件循环
  43. event_base_loopexit(base, NULL);
  44. }
  45. //添加事件到地基上
  46. event_add(connev, NULL);
  47. }
  48. int main()
  49. {
  50. //创建套接字
  51. int lfd = socket(AF_INET, SOCK_STREAM, 0);
  52. if (lfd < 0) {
  53. printf("create socket error!");
  54. exit(0);
  55. }
  56. //设置端口复用
  57. int opt = 1;
  58. setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
  59. //绑定本地地址
  60. struct sockaddr_in serv;
  61. bzero(&serv, sizeof(serv));//初始化
  62. serv.sin_addr.s_addr = htonl(INADDR_ANY);
  63. serv.sin_port = htons(10066);
  64. serv.sin_family = AF_INET;
  65. int rent = bind(lfd, (struct sockaddr*)&serv, sizeof(serv));
  66. if (rent < 0) {
  67. printf("bind addr error!");
  68. exit(0);
  69. }
  70. //监听文件描述符
  71. rent = listen(lfd, 128);
  72. if (rent < 0) {
  73. printf("listen socket error");
  74. exit(0);
  75. }
  76. //创建地基
  77. struct event_base* base = event_base_new();
  78. if (base == NULL) {
  79. printf("地基创建失败!\n");
  80. exit(0);
  81. }
  82. //创建事件,以base为基础,检测文件描述符lfd的读事件,并且持续检测,如果有动静则启动回调函数func
  83. //并且将地基base传入到func中,这样就可以在回调函数中对其进行操作了
  84. struct event* ev = event_new(base, lfd, EV_READ | EV_PERSIST, func, base);
  85. if (ev == NULL) {
  86. printf("创建事件失败!\n");
  87. exit(0);
  88. }
  89. //将事件加入到地基base中,持续时间这里无限长
  90. event_add(ev, NULL);
  91. //进入事件循环等待事件发生
  92. event_base_dispatch(base);
  93. //释放资源
  94. event_base_free(base);
  95. event_free(ev);
  96. //关闭文件描述符
  97. close(lfd);
  98. return 0;
  99. }

Libevent [补档-2023-08-29]的更多相关文章

  1. STL 补档

    STL 补档 1.vector 作用:它能够像容器一样存放各种类型的对象,简单地说,vector是一个能够存放任意类型的动态数组,能够增加和压缩数据. vector在C++标准模板库中的部分内容,它是 ...

  2. 图论补档——KM算法+稳定婚姻问题

    突然发现考前复习图论的时候直接把 KM 和 稳定婚姻 给跳了--emmm 结果现在刷训练指南就疯狂补档.QAQ. KM算法--二分图最大带权匹配 提出问题 (不严谨定义,理解即可) 二分图 定义:将点 ...

  3. Java 高效编程(Effective Java)中文第三版(补档)

    来源:sjsdfg/effective-java-3rd-chinese <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过, ...

  4. [补档] 大假期集训Part.1

    新博客搭起来先补一发档... 那就从大假期集训第一部分说起好了QwQ 自己还是太菜掉回了2016级水平 day1: day1的时候来得有点晚(毕竟准高一)然后进机房发现早就开考了还没有给我题面于是搞了 ...

  5. 软件安装配置笔记(三)——ArcGIS系列产品安装与配置(补档)(附数据库连接及数据导入)

    在前两篇安装配置笔记之后,就忘记把其他安装配置笔记迁移过来了,真是失误失误!趁现在其他文档需要赶紧补上. 目录: 一.ArcMap 二.ArcMap连接数据库并导入数据 三.Arcgis Pro 四. ...

  6. libevent文档学习(一)多线程接口和使用

    参考libevent官方提供的文档: http://www.wangafu.net/~nickm/libevent-book/Ref1_libsetup.html 这一篇主要翻译libevent多线程 ...

  7. [补档]暑假集训D6总结

    考试 不是爆零,胜似爆零= = 三道题,就拿了20分,根本没法玩好吧= = 本来以为打了道正解,打了道暴力,加上个特判分,应该不会死的太惨,然而--为啥我只有特判分啊- - 真的是惨. 讲完题觉得题是 ...

  8. [补档]暑假集训D5总结

    %dalao 今天又有dalao来讲课,讲的是网络流 网络流--从入门到放弃:7-29dalao讲课笔记--https://hzoi-mafia.github.io/2017/07/29/27/   ...

  9. <2014 08 29> MATLAB的软件结构与模块、工具箱简示

    MATLAB的系统结构:三个层次.九个部分 ----------------------------------- 一.基础层 是整个系统的基础,核心内容是MATLAB部分. 1.软件主包MATLAB ...

  10. 补档 Codeblocks下的文件标题栏(标签)显示方法

    可能在以下链接也能看到这篇文档 我知道很多人都不知道这个到底叫啥,还不如直接一点: 文件标题栏 就是如下的效果. 解决办法: 在左上角第三个view下,打开后取消Hide editor tabs 选项 ...

随机推荐

  1. 微信公众号短链实时阅读量、点赞数爬虫(不会Hook可用)

    众所周知,微信分享的公众号分享出的一般都是短链,在这个锻炼下使用浏览器打开并不能获取微信公众的阅读量点赞数等这些信息,如图1所示. 但是实际拥有详细信息的则是这个链接下面,提取链接所需要提交的信息包括 ...

  2. MaxListenersExceededWarning:Possible EventEmitter memory leak detected.

    打包出现内存溢出 解决办法:

  3. Vue第三篇 Vue组件

    01-组件的全局注册 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&quo ...

  4. maven开源仓库

    在公司开发一般都用公司内部的maven仓库,但回家之后,就访问不了公司的网络,使用不了公司的maven仓库,只能使用开源的maven仓库. 在网上搜索和整理了几个比较好用的maven开源镜像仓库,记录 ...

  5. Telegraf 使用小结

    转载请注明出处: 1.简介: Telegraf是一个开源的代理程序,用于收集.处理.汇总和发送指标数据.它可以与不同的数据存储和可视化工具(如InfluxDB.Elasticsearch.Grafan ...

  6. 13个构建RESTful API的最佳实践

    前言 Facebook.GitHub.Google和其他许多巨头都需要一种方法来服务和消费数据.在今天的开发环境中,RESTful API仍然是服务和消费数据的最佳选择之一. 但你是否考虑过学习行业标 ...

  7. 两个List< string>比较是否相同的N种方法,你用过哪种?

    今天在一技术群看一群大佬讨论: 有没有优雅的写法,比较两个List集合中的元素是不是完全一致... 站长最近也无聊,通过群里的聊天记录和给出的参考链接,简单做做总结,万一后面大家能用上呢? 我们做简单 ...

  8. 阿里云IPV6 创建虚拟机的过程

    阿里云IPV6 创建虚拟机的过程 背景 IPV6 已经越来越广泛的应用. 想在外网开通一下IPV6,发现还有一些坑. 这里总结一下. 备忘. 开通方式 1. 登录阿里云的控制台, 打开云服务器ECS的 ...

  9. SkyWalking的学习之二(性能优化以及log)

    SkyWalking的学习之二(性能优化以及log) 背景 周六在家学习了SkyWalking的交单部署和agent的方式获取日志. 万恶的周天上班到公司发现出现了宕机. 具体原因是我想进行SkyWa ...

  10. [转帖]ORACLE等待事件:enq: TX - row lock contention

    https://www.cnblogs.com/kerrycode/p/5887150.html enq: TX - row lock contention等待事件,这个是数据库里面一个比较常见的等待 ...