linux入门到精通-第十九章-libevent(开源高性能事件通知库)

目录

  • 参考
  • 什么是libevent
    • 应用
    • 核心实现
    • libevent的地基event_base
    • 等待事件产生,循环监听event_loop
    • 退出循环监听event_base_loopexit
    • 创建事件
    • 工作流程
  • 安装一(源码安装,推荐)
    • 现在源码
    • 配置
    • 编译
    • 安装
    • 验证安装
  • 安装二(可能因为openssl报错)
    • 参考
    • 下载安装包
    • 配置
    • 编译
    • 安装
    • 测试libevent是否安装成功:
  • samples
    • 测试method.c
      • 代码
      • 编译执行
    • 服务端程序

参考

视频教程
libevent的基本使用

什么是libevent

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

应用

Chromium、Memcached、NTP、HTTPSQS等著名的开源程序都使用libevent库,足见libevent的稳定。更多使用libevent的程序可以到libevent的官网查看。

核心实现

Reactor(反应堆)模式是libevent的核心框架,libevent以事件驱动,自动触发回调功能。之前介绍的epoll反应堆源码,就是从libevent中抽取出来的。

libevent的地基event_base

在使用libevent这个库的时候,就像我们盖房子一样,需要一个地基,我们这个接口就是来完成这个工作的,在他的基础上会有的一个事件集合去检查哪一个事件是激活的

// 通过以下函数获得event_base结构
struct event_base * event_base_new(void);
// 申请到的指针可以通过event_base_free释放
event_base_free(struct event_base *)// 如果fork出子进程,想在子进程继续使用event_base,那么子进程需要对event_base重新初始化,函数如下:
int event_reinit(struct event_base *base);

等待事件产生,循环监听event_loop

他类似于while(1)的功能,去循环监视事件的发生
int event_base_dispatch(struct event_base *base);
调用该函数(相当于while(1) {epoll_wait}),相当于没有设置标志位的event_base_loop。程序将会一直运行,直到没有需要检测的事件了,或者被结束循环的api终止。

int event_base_dispatch(struct event_base *base);
调用该函数,相当于没有设置标志位的event_base_loop。程序将会一直运行,直到
没有需要检测的事件了,或者被结束循环的api终止。int event_base_loopexit(struct event_base *base, const struct timeval *tv);
int event_base_loopbreak(struct event_base *base);
struct timeval {long    tv_sec;                    long    tv_usec;两个函数的区别是如果正在执行激活事件的回调函数,那么event_base_loopexit将在事件回调执行结束后终止循环(如果tv时间非NULL,那么将等待tv设置的时间后立即结束循环),而event_base_loopbreak会立即终止循环。            

退出循环监听event_base_loopexit

int event_base_loopexit(struct event_base *base, const struct timeval *tv);
int event_base_loopbreak(struct event_base *base);
struct timeval {long    tv_sec;                    long    tv_usec;两个函数的区别是如果正在执行激活事件的回调函数,那么event_base_loopexit将在事件回调执行结束后终止循环(如果tv时间非NULL,那么将等待tv设置的时间后立即结束循环),而event_base_loopbreak会立即终止循环。            

创建事件

struct event *event_new(struct event_base *base, evutil_socket_t fd,short events, event_callback_fn cb, void *arg);
event_new负责新创建event结构指针,同时指定对应的地基base,还有对应的文件描述符,
事件,以及回调函数和回调函数的参数。参数说明:
base 对应的根节点
fd 要监听的文件描述符
events 要监听的事件
#define  EV_TIMEOUT         0x01   //超时事件
#define  EV_READ                  0x02 //读事件
#define  EV_WRITE                0x04  //写事件
#define  EV_SIGNAL              0x08     //信号事件
#define  EV_PERSIST              0x10   //周期性触发
#define  EV_ET                        0x20 //边缘触发,如果底层模型支持
cb 回调函数,原型如下:
typedef void (*event_callback_fn)(evutil_socket_t fd, short events, void *arg);
arg 回调函数的参数
7.	int event_add(struct event *ev, const struct timeval *timeout);
将非未决态事件转为未决态,相当于调用epoll_ctl函数,开始监听事件是否产生。
参数说明:Ev 就是前面event_new创建的事件
Timeout 限时等待事件的产生,也可以设置为NULL,没有限时。
8.	int event_del(struct event *ev);
将事件从未决态变为非未决态,相当于epoll的下树(epoll_ctl调用EPOLL_CTL_DEL操作)操作。
9.	void event_free(struct event *ev);
释放event_new申请的event节点。

工作流程

在这里插入图片描述

安装一(源码安装,推荐)

现在源码

cd /usr/local/clib/libevent/
git clone https://github.com/nmathewson/Libevent.git

配置

sh autogen.sh
./configure --prefix=/usr/local/clib/libevent/libevent-2.0.12

编译

make -j4

安装

make install

验证安装

make verify  //验证安装

安装二(可能因为openssl报错)

参考

libevent下载与安装学习整理

下载安装包

官网:http://www.monkey.org/~provos…
下载:libevent-2.1.12-stable.tar.gz
解压

 
cd /usr/local/clib/libevent
# 下载
wget https://www.monkey.org/~provos/libevent-2.0.12-stable.tar.gz
tar zxvf libevent-2.0.12-stable.tar.gz
# 

配置

./configure --prefix=/usr/local/clib/libevent/libevent-2.0.12 --with-openssl=/usr/local/clib/openssl/1.1.1o

编译

make -j4

安装

 make install

测试libevent是否安装成功:

# ls -al /usr/lib | grep libevent

samples

在这里插入图片描述

cd /usr/local/clib/libevent/Libevent/sample

测试method.c

打印支持的方法

代码

#include<event.h>
#include<stdio.h>int main()
{// 获取libevent后端支持的方法const char **methods = event_get_supported_methods();for(int i = 0;methods[i] != NULL;i++){printf("method:%s\n", methods[i]);}struct event_base* base = event_base_new();printf("%s\n", event_base_get_method(base));return 0;
}

编译执行


# 编译
gcc -I/usr/local/clib/libevent/libevent-2.0.12/include -L/usr/local/clib/libevent/libevent-2.0.12/lib hello-world.c -levent
# 运行
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/clib/libevent/libevent-2.0.12/lib
./a.out

服务端程序

#include<event.h>
#include <sys/socket.h> // socket依赖
#include <arpa/inet.h>  // socket依赖
#include<stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>     // close依赖
#include <errno.h>
#include <string.h>#define MAX_LISTEN_SOCKET 1   /*监听上限*/
#define MAX_LISTEN_EVENTS 100 /*监听上限*/
#define SOCKET_PORT 8000      /*端口号*/
#define BUF_SIZE 1024         /*缓存区大小*/
#define READ_BUF_SIZE 4         /*缓存区大小*/typedef struct T_EVENT
{int fd;                                           // 要监听的文件描述符struct event_base * base;                                       // 对应的监听事件,EPOLLIN和EPLLOUTstruct event *ev;  long last_active; // 记录每次加入红黑树 g_efd 的时间值
}T_EVENT, *PT_EVENT;/*** @brief * 客户端事件* @param fd * @param event * @param arg */
void clientFun(int fd, short events, void* arg){// 参数转换PT_EVENT e =  (PT_EVENT)arg;struct event_base * base = e->base;int len = -1;// 开始循环度int buf_len = 0;char buf[BUF_SIZE] = {0};struct event *ev = e->ev;// 先下树event_del(ev);int is_close = 0;while (1){len = recv(fd, buf+buf_len, READ_BUF_SIZE, 0); // 读取客户端发过来的数据if (len > 0){buf_len += len;printf("C[%d] say: len:%d, content:%s\n", fd, len,buf);}else if (len == 0){is_close = 1;printf("[fd=%d]  closed\n", fd);break;}else{// 如果是读缓冲区读干净了,这个时候应该跳出while循环if (errno == EAGAIN){break;}is_close = 1;printf("recv[fd=%d] error[%d]:%s\n", fd, errno, strerror(errno));break;}}printf("客户端已经循环读完,is_close:%d\n",is_close);if(is_close){close(fd);// 释放free(e);}else{len = send(fd, buf, buf_len, 0); // 直接将数据回射给客户端// 再次上树event_add(ev, NULL);}
}void serverFun(int fd, short event, void* arg){// 参数转换struct event_base * base = (struct event_base *)arg;struct sockaddr_in cin;socklen_t len = sizeof(cin);// 提取新的cfdint cfd = -1;if ((cfd = accept(fd, (struct sockaddr *)&cin, &len)) == -1){if (errno != EAGAIN && errno != EINTR){sleep(1);}printf("%s:accept,%s\n", __func__, strerror(errno));return;}printf("new connect[%s:%d]\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));int flag = 0;// 设置客户端为非阻塞int flags = fcntl(cfd, F_GETFL); // 获取的cfd的标志位flags |= O_NONBLOCK;if ((flag = fcntl(cfd, F_SETFL, flags)) < 0) // 将cfd也设置为非阻塞{printf("%s: fcntl nonblocking failed, %s\n", __func__, strerror(errno));}// 初始化上树节点和事件PT_EVENT e = malloc(sizeof(T_EVENT));e->fd = cfd;e->base = base;struct event *ev = event_new(base, cfd, EV_READ | EV_ET | EV_PERSIST, clientFun, e);e->ev = ev;// 上树event_add(ev, NULL);
}int main()
{int sockfd = 0;int ret = -1;sockfd = socket(AF_INET, SOCK_STREAM, 0);printf("监听套接字文件描述符:%d\n", sockfd);struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(SOCKET_PORT);// addr.sin_addr.s_addr = inet_addr("127.0.0.1");addr.sin_addr.s_addr = htons(INADDR_ANY);// 一般在一个端口释放后需要等一段时间才能重新启用,因此需要借助SO_REUSEADDR来使端口重新启用。解决服务端异常退出之后,再次启动服务端,客户端无法使用同一个端口连接socket的问题// 设置端口复用int opt = 1;ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));if (ret == -1){perror("设置端口复用失败!\n");return -1;}ret = bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));if (ret == -1){perror("绑定失败!\n");return -1;}ret = listen(sockfd, 5);if (ret == -1){perror("监听失败!\n");return -1;}// 注册event_base根节点,相当于epoll_craetestruct event_base * base = event_base_new();// 初始化sockfd上树节点struct event *ev = event_new(base, sockfd, EV_READ | EV_ET | EV_PERSIST, serverFun, base);// 上树event_add(ev, NULL);// 循环监听, 阻塞event_base_dispatch(base);// 收尾close(sockfd);event_base_free(base);}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/841948.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

KingbaseES数据库merge语法

数据库版本&#xff1a;KingbaseES V008R006C008B0014 简介 MERGE 语句是一种用于数据操作的 SQL 语句&#xff0c;它能够根据指定的条件将 INSERT、UPDATE 和 DELETE 操作结合到单个语句中。其主要作用是在目标表和源表之间进行数据比较和同步&#xff0c;根据条件的匹配情况来…

高效并发编程:Java阻塞队列深度解析与最佳实践

1.阻塞队列的基本概念与应用场景 1.1 阻塞队列的定义 阻塞队列&#xff08;BlockingQueue&#xff09;是Java并发包中的一个接口&#xff0c;它支持两个附加操作&#xff1a;当队列为空时&#xff0c;获取元素的线程会等待队列变为非空&#xff1b;当队列满时&#xff0c;存储…

RAG概述(二):Advanced RAG 高级RAG

目录 概述 Advanced RAG Pre-Retrieval预检索 优化索引 增强数据粒度 粗粒度 细粒度 展开说说 优化索引 Chunk策略 Small2Big方法 元数据 引入假设性问题 对齐优化 混合检索 查询优化 查询扩展 查询转换 Post-Retrieval后检索 参考 概述 Native RAG&#…

转义字符知识点

转义字符的使用 什么是转义字符&#xff1f; 它是字符串的一部分&#xff0c;用来表示一些特殊含义的字符 比如&#xff1a;在字符串中表现&#xff0c;单引号&#xff0c;引号&#xff0c;空行等等 固定写法 \字符 不同的\和字符的组合表示不同的含义. 常用的转义字符 附…

关于Springboot同时上传文件与其他参数

Springboot同时上传文件与其他参数 http请求数据传递分为请求体和请求参数。跟在url后面的为请求参数&#xff0c;文件上传和json数据放在请求体中。请求参数格式固定&#xff0c;后台可直接解析&#xff1b;请求体中的数据后台需要根据content-type字段按照特定格式解析。con…

shell从入门到精通(23)贪婪匹配、非贪婪以及独占模式

文章目录 贪婪与非贪婪的区别示例贪婪匹配的特点--自动回溯以满足匹配独占模式总结贪婪与非贪婪的区别 在正则表达式中,贪婪匹配和非贪婪匹配是指匹配模式下的不同行为。 贪婪匹配: 贪婪匹配会尽可能多地匹配符合模式的字符。换句话说,它会一直匹配直到无法再匹配为止。例…

springboot vue 开源 会员收银系统 (4) 门店模块开发

前言 完整版演示 前面我们对会员系统 springboot vue 开源 会员收银系统 (3) 会员管理的开发 实现了简单的会员添加 下面我们将从会员模块进行延伸 门店模块的开发 首先我们先分析一下常见门店的管理模式 常见的管理形式为总公司 - 区域管理&#xff08;若干个门店&#xff…

Java实现插入排序、冒泡排序、堆排序、希尔排序、选择排序、优先队列排序、快速排序、归并排序(详细注释,原理解析)

插入排序 package learn;import java.util.Arrays;/** 每次都将当前元素插入到左侧已经排序的数组中&#xff0c;使得插入之后左侧数组依然有序。* 速度优于选择排序*/ public class InsertSort {public static void insertSort(int[] a) {int n a.length;for (int i 1; i &…

Elasticsearch优点和缺点以及要点和难点具体应用

Elasticsearch是一个开源、分布式、实时的搜索和分析引擎,它位于Elastic Stack(以前称为ELK Stack)的核心。以下是关于Elasticsearch的一些主要特点和功能: 1.分布式和可扩展性:Elasticsearch是分布式的,可以轻松扩展到多个节点以处理大规模数据集和高并发请求。通过将数…

C语言 | Leetcode C语言题解之第113题路径总和II

题目&#xff1a; 题解&#xff1a; int** ret; int retSize; int* retColSize;int* path; int pathSize;typedef struct {struct TreeNode* key;struct TreeNode* val;UT_hash_handle hh; } hashTable;hashTable* parent;void insertHashTable(struct TreeNode* x, struct Tr…

C++干货 --类和对象(二)

前言&#xff1a; 上文中&#xff0c;我们介绍了类这一重要知识点&#xff0c;包括为什么要有类、类的使用方法、封装、以及对象实例化。详情可以去看我的文章&#xff1a;写文章-CSDN创作中心C干货 --类和对象(一)-CSDN博客写文章-CSDN创作中心 这篇文章&#xff0c;我们简单…

我学会了用插件来辅助PostgreSQL,可拷,很刑!

作者&#xff1a;IT邦德 中国DBA联盟(ACDU)成员&#xff0c;10余年DBA工作经验&#xff0c; Oracle、PostgreSQL ACE CSDN博客专家及B站知名UP主&#xff0c;全网粉丝10万 擅长主流Oracle、MySQL、PG、高斯及Greenplum备份恢复&#xff0c; 安装迁移&#xff0c;性能优化、故障…

【蓝桥杯省赛真题44】python计算N+N的值 中小学青少年组蓝桥杯比赛 算法思维python编程省赛真题解析

目录 python计算NN的值 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 七、 推荐资料 1、蓝桥杯比赛 2、考级资料 3、其它资料 python计算NN的值 第十四届蓝桥杯青少年组python省赛真题 一、题目要求…

Makefile学习笔记18|u-boot顶层Makefile04

Makefile学习笔记18|u-boot顶层Makefile04 希望看到这篇文章的朋友能在评论区留下宝贵的建议来让我们共同成长&#xff0c;谢谢。 这里是目录 C语言标准 # With the move to GCC 6, we have implicitly upgraded our language # standard to GNU11 (see https://gcc.gnu.org/…

WordPress--解决迁移后页面404(打不开)的问题

原文网址&#xff1a;WordPress--解决迁移后页面404的问题_IT利刃出鞘的博客-CSDN博客 简介 本文介绍WordPress迁移后部分页面404(打不开)的解决方案。 方案&#xff1a;修改固定链接 简介 要在刚建好网站&#xff0c;还没导入数据之前修改固定链接&#xff0c;否则可能导…

【教学类-58-05】黑白三角拼图05(2-10宫格,每个宫格随机1张-6张,带空格纸,1页3张黑白3张白卡)

背景需求&#xff1a; 【教学类-58-04】黑白三角拼图04&#xff08;2-10宫格&#xff0c;每个宫格随机1张-6张&#xff0c;带空格纸&#xff0c;1页6张黑白&#xff0c;1张6张白卡&#xff09;-CSDN博客文章浏览阅读582次&#xff0c;点赞16次&#xff0c;收藏3次。【教学类-58…

Mac安装pytorch

先下载 Anaconda | The Operating System for AI 网速慢&#xff0c;用中国大陆镜像&#xff1a;NJU Mirror 之前装python3时用的是pip3&#xff0c;这里说一下这pip与conda的区别 Conda和pip都是Python包管理工具&#xff0c;用于安装和管理Python包 包管理范围&#xff1a…

汽车数据应用构想(一)

自从电动汽车GB/T32960标准颁布&#xff0c;要求所有电动汽车必须上传数据开始&#xff0c;各车厂就开始花费大量的人力物力&#xff0c;用于数据的上传与存储。同时随着智能化、网联化的趋势&#xff0c;不断丰富上传数据的内容与数量。数据已成为车厂的重要资产&#xff0c;但…

Day16

Day16 一、迭代器 深入迭代器-foreach的底层 for (String element : list) {System.out.println(element);}底层&#xff1a; //使用foreach循环遍历集合的底层实现&#xff1a;String element;for(Iterator it list.iterator();it.hasNext();System.out.println(element))e…

微服务:eureka的搭建,以及服务注册、服务发现、负载均衡

eureka 搭建 新建一个Module,maven项目&#xff0c;导入依赖。 <dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency><…