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. AtCoder | ABC 125 Person Editorial

    开始补AtCoder的数学题了,练下思维 AB两道都很简单,看懂题就OK. C,D稍微麻烦一些 Problem C: GCD On Blackboard 为了解决此问题,我们需要了解最大公约数(GCD ...

  2. webpack升级-心得

  3. 我让 ChatGPT 化身为全知全能的文档小助理,啥姿势她都会......

    ChatGPT 虽然只是一个对话型人工智能,但已经震惊了全世界,有人甚至认为人工智能的奇点已经到来.未来一定会有很多人失业,从工业革命开始,每出现一次重大的技术变革,就必然会有一批人失业,我们要直面现 ...

  4. Ribbon默认负载均衡规则替换为NacosRule

    近期博主在参与一个 Spring Cloud 搭建,版本为 Hoxton.SR12,服务注册发现组件为 Nacos 的老项目时,发现项目负载均衡组件 Ribbon 的负载均衡规则在某些场景下不够完美, ...

  5. 【Hash】字符串哈希

    Hash 的核心思想在于,将输入映射到一个值域较小.可以方便比较的范围,典型的用法就是将资源紧张的设备中的不定长字符串转化为定长整数,以达到节省空间的目的 如:printf("This is ...

  6. javaweb 项目!号 解决方案

    1:右击项目工程名称2:Properties3:  Jvav Build Path4:  Libraries5:  Add External JARS6:  找到"E:\apache-tom ...

  7. mysql-数值函数-取整-保留小数位-求余数

  8. [转帖]elasticsearch-create-enrollment-tokenedit

    https://www.elastic.co/guide/en/elasticsearch/reference/current/create-enrollment-token.html The ela ...

  9. [转帖]shell脚本实现文本内容比较交互程序

    背景介绍 脚本基于Comm命令进行功能封装,考虑到命令执行前需要对文本进行排序,并且在多文件需要比较内容时可能会导致多个文本混乱,因此使用Shell封装成了一个交互式程序,快速对文件内容进行判断和输出 ...

  10. [转帖]金仓数据库KingbaseES V8R6 中unlogged表

    KingbaseESV8R6有一种表称为unlogged,在该表新建的索引也属于unlogged.和普通表的区别是,对该表进行DML操作时候不将该表的变更记录变更写入到wal文件中.在数据库异常关机或 ...