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 服务端代码实例:

点击查看代码

#include <event2/event.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h> struct event* connev = NULL;//全局变量事件 //回调函数 void fun(evutil_socket_t fd, short events, void* arg) { int n; char data[128];//接收来自客户端的数据 char serv_data[256] = "服务器已经收到你的来信!"; //接收并且处理数据 n = recv(fd, data, sizeof(data), 0); if (n < 0) { ​ printf("接收数据失败!\n"); ​ //记得删除事件 ​ event_del(connev); ​ close(fd); } else { ​ if (send(fd, serv_data, sizeof(serv_data), 0) == -1) { ​ printf("数据发送失败\n"); ​ event_del(connev); ​ close(fd); ​ } } } //回调函数 void func(evutil_socket_t fd, short events, void* arg) { //调用func时传入了地基base,然后需要我们将其赋值给func中的base,以便访问 struct event_base* base = (struct event_base*)arg; //接收新的客户端连接 int cfd = accept(fd, NULL, NULL); if (cfd < 0) { ​ printf("建立连接失败!\n"); ​ return; } //新建事件,还是以base为地基,然后持续监听与客户端连接的cfd的读缓冲区 //如果有动静则调用func connev = event_new(base, cfd, EV_READ | EV_PERSIST, fun, NULL); if (connev == NULL) { ​ printf("error\n"); ​ //退出事件循环 ​ event_base_loopexit(base, NULL); } //添加事件到地基上 event_add(connev, NULL); } int main() { //创建套接字 int lfd = socket(AF_INET, SOCK_STREAM, 0); if (lfd < 0) { ​ printf("create socket error!"); ​ exit(0); } //设置端口复用 int opt = 1; setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); //绑定本地地址 struct sockaddr_in serv; bzero(&serv, sizeof(serv));//初始化 serv.sin_addr.s_addr = htonl(INADDR_ANY); serv.sin_port = htons(10066); serv.sin_family = AF_INET; int rent = bind(lfd, (struct sockaddr*)&serv, sizeof(serv)); if (rent < 0) { ​ printf("bind addr error!"); ​ exit(0); } //监听文件描述符 rent = listen(lfd, 128); if (rent < 0) { ​ printf("listen socket error"); ​ exit(0); } //创建地基 struct event_base* base = event_base_new(); if (base == NULL) { ​ printf("地基创建失败!\n"); ​ exit(0); } //创建事件,以base为基础,检测文件描述符lfd的读事件,并且持续检测,如果有动静则启动回调函数func //并且将地基base传入到func中,这样就可以在回调函数中对其进行操作了 struct event* ev = event_new(base, lfd, EV_READ | EV_PERSIST, func, base); if (ev == NULL) { ​ printf("创建事件失败!\n"); ​ exit(0); } //将事件加入到地基base中,持续时间这里无限长 event_add(ev, NULL); //进入事件循环等待事件发生 event_base_dispatch(base); //释放资源 event_base_free(base); event_free(ev); //关闭文件描述符 close(lfd); return 0; }

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. Seata是什么?一文了解其实现原理

    一.背景 随着业务发展,单体系统逐渐无法满足业务的需求,分布式架构逐渐成为大型互联网平台首选.伴随而来的问题是,本地事务方案已经无法满足,分布式事务相关规范和框架应运而生. 在这种情况下,大型厂商根据 ...

  2. HTTP Headers Content-Type 详解

    https://www.cnblogs.com/whosmeya/p/14315632.html

  3. P5719 水题

    https://www.luogu.com.cn/problem/P5719 唠唠:别看这题很水,且只要求保留小数点后一位,倘若用float而不是double的话就无法AC,洛谷评测则只有40分.所以 ...

  4. KSP(坎巴拉)萌新感悟

    1.为什么降落月球等无大气星球时减速为亚轨道之后便不再减速,等到快坠落的时候在满节流阀极限减速最省燃料? 因为我们的dv是确定的,燃料能给我们带来一定量的冲量,却因此可以带来不一定量的动量,显然速度越 ...

  5. @Configuration 注解使用及源码解析

    本文为博主原创,转载请注明出处: @Configuration 注解对我们来说并不陌生,以javaConfig的方式定义spring IOC容器的配置类使用的就是这个@Configuration. s ...

  6. [转帖]记一次探索内存cache优化之旅

    https://developer.aliyun.com/article/972803 背景 项目上线以来,曾出现上传镜像.下发镜像时可用内存不足,性能发生抖动的情况.研究发现是容器的 page ca ...

  7. [转帖]Nginx动静分离;资源分离;rewrite重写、跳转、伪静态、规则、日志

    https://www.cnblogs.com/caodan01/p/14745562.html 一.动静分离 动静分离,通过中间件将动静请求和静态请求进行分离: 通过中间件将动态请求和静态请求分离, ...

  8. [转帖]Jmeter中线程组和setUP线程组、tearDown线程组的区别

    JMETER: setUP线程组:在测试任务ThreadGroup 运行前先被运行.通常用在运行测试任务前,做初始化工作.例如建立数据库连接初始分化工作.用户登录 tearDown线程组:在测试任务线 ...

  9. [转帖]Kafka—配置SASL/PLAIN认证客户端及常用操作命令

    介绍   SASL/PLAIN 是一种简单的 username/password安全认证机制,本文主要总结服务端开启该认证后,命令行客户端进行配置的操作流程. 配置 增加jaas.properties ...

  10. shell的date的部分处理--需要记住..

    在Linux中,可以使用date命令获取日期, date 获取当前完整日期 date --date="3 days ago" 获取3天前的完整日期 date --date=&quo ...