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

news2024/11/17 13:19:40

目录

  • 参考
  • 什么是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和EPLLOUT
    struct 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);
    // 提取新的cfd
    int 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_craete
    struct 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.coloradmin.cn/o/1700314.html

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

相关文章

【记忆空间】 史上最详细,移动固态硬盘,西数BLACK D30拆解,测评,外观与内饰解析。第一期:外观与内饰。

【记忆空间】 史上最详细&#xff0c;移动固态硬盘&#xff0c;西数BLACK D30拆解&#xff0c;测评&#xff0c;外观与内饰解析。第一期&#xff1a;外观与内饰。 外观实拍&#xff0c;正面&#xff0c;整体黑色&#xff0c;外壳是一个塑料的材质。 外观实拍&#xff1a;侧面…

Android Studio开发之路(十四)自定义Titlebar以及设置顶部状态栏颜色

一、描述 项目需求&#xff0c;我要做一个下图这样的titlebar,包括一个返回按钮&#xff0c;一个关闭按钮&#xff0c;一个文本框。默认的titlebar按钮设计不太满足我的需求&#xff0c;于是我打算自定义一个titlebar组件&#xff0c;应用到我的每一个页面 二、titlebar组件设…

新计划,不断变更!做自己,接受不美好!猪肝移植——早读(逆天打工人爬取热门微信文章解读)

时间不等人 引言Python 代码第一篇 做自己&#xff0c;没有很好也没关系第二篇结尾 引言 新计划&#xff1a; 早上一次性发几个视频不现实 所以更改一下 待后面有比较稳定的框架再优化 每天早上更新 早到8点 晚到10点 你刚刚好上班或者上课 然后偷瞄的看两眼 学习一下 补充知…

HCIP-Datacom-ARST自选题库__BGP判断【20道题】

1.传统的BGP-4只能管理IPV4单播路由信息&#xff0c;MP-BGP为了提供对多种网络层协议的支持&#xff0c;对BGP-4进行了扩展。其中MP-BGP对IPv6单播网络的支持特性称为BGP4&#xff0c;BGP4通过Next Hop属性携带路由下一跳地址信息。 2.BGP4通过Update报文中的Next Hop属性携带…

C++之运算符重载

在C中&#xff0c;运算符重载是一种特殊的函数&#xff0c;它允许程序员重新定义或重载大部分内置运算符&#xff0c;以便它们可以用于用户自定义的数据类型。通过运算符重载&#xff0c;可以使自定义类型的使用更加直观和自然。 不是所有运算符都可以被重载&#xff1a; .&a…

聊聊变异测试

软件质量保障 所寫即所思&#xff5c;一个阿里质量人对测试的所感所悟。 1. 介绍 有句话说&#xff1a;证实容易&#xff0c;证伪难。正如测试一样&#xff0c;证明缺陷存在容易&#xff0c;但证明不存在缺陷难。而变异测试颠覆了这一原则&#xff0c;如果我们知道存在缺陷&am…

ARM+FPGA+NVIDIA AI摄像头软硬件定制

拥有资深ISP图像技术团队&#xff0c;是英伟达、地平线等合作伙伴&#xff0c;我们的团队掌握目前市面上大部分车载平台的ISP图像画质服务能力&#xff0c;能自主开发图像ISP和增强算法。我们具有多名经验丰富光学设计专家&#xff0c;掌握车载模组光学设计能力&#xff0c;资深…

数据结构——链表——模板类实现双向链表——先完成再完美——持续更

链表&#xff1a;概念&#xff0c;实现&#xff0c;《数据结构》这里实现是基于模板的 C语言基础&#xff0c;指针&#xff0c;引用。模板。《CPrimer》有些进阶用法放在语言学习的目录 LeetCode应用&#xff0c;会更新在LeetCode150&#xff0c;目前这个系列先暂停&#xff0c…

遇见问题-VMware虚拟机打开运行一段时间后卡死

1.问题原因 因为Windows自带的虚拟化软件Hyper-V与VMware存在冲突。 2.关闭Hyper-V 1.打开【控制面板】-【程序和功能】-【启用或关闭Windows功能】3.关闭HV主机服务 1.右击计算机-》管理-》服务和应用名称-》服务-》找到HV主机服务-》右击属性停止服务 -》启动类型设置为禁…

NASA数据集——阿尔法喷气式大气实验二氧化碳和甲烷数据

Alpha Jet Atmospheric eXperiment Carbon Dioxide and Methane Data 阿尔法喷气式大气实验二氧化碳和甲烷数据 简介 Alpha Jet Atmospheric eXperiment (AJAX) 是美国国家航空航天局艾姆斯研究中心与 H211, L.L.C. 公司的合作项目&#xff0c;旨在促进对加利福尼亚、内华达…

asp.net core接入prometheus2-自定义指标

前提 了解一下asp.net core接入prometheus快速入门 https://blog.csdn.net/qq_36437991/article/details/139064138 新建.net 8空web项目 安装下面三个包 <PackageReference Include"OpenTelemetry.Exporter.Prometheus.AspNetCore" Version"1.8.0-rc.1&…

【软件设计师】计算机组成原理

1、数据的表示 1.1 进制转换 整型有4种进制形式&#xff1a; 1.十进制&#xff08;D&#xff09;&#xff1a; 都是以0-9这九个数字组成&#xff0c;不能以0开头。 2.二进制&#xff08;B&#xff09;&#xff1a; 由0和1两个数字组成。 3.八进制&#xff08;O&#xff09;&am…

Java的类路径究竟是什么?

回答 问了chatgpt这个问题&#xff0c;首先类路径的定义是&#xff1a; 是指一组路径&#xff0c;这些路径告诉Java虚拟机&#xff08;JVM&#xff09;和类加载器在哪里可以找到应用程序所需的类和资源文件。说白了就是在运行java程序的时候需要先将java源代码编译成class文件…

代码随想录——从前序与中序遍历序列构造二叉树(Leetcode105)

题目链接 递归 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, TreeNode left, TreeNode right) {* …

nginx + confd + etcd 实现nginx动态白名单,防止请求host篡改注入攻击

众所周知&#xff0c;浏览器发送到后台请求header中携带的host是可能被修改的&#xff0c;为了防止这件事情发生&#xff0c;就很有必要给后台设置host请求白名单了&#xff0c;本次将介绍nginx如何配置&#xff0c;以及结合confd和etcd实现动态配置。 1. http/https请求的host…

flink cdc mysql整理与总结

文章目录 一、业务中常见的需要数据同步的场景CDC是什么FlinkCDC是什么CDC原理为什么是FlinkCDC业务场景flink cdc对应flink的版本 二、模拟案例1.阿里云flink sql2.开源flink sql(单机模式)flink 安装安装mysql3.flink datastream 三、总结 提示&#xff1a;以下是本篇文章正文…

MFC扩展库BCGControlBar Pro v34.1新版亮点:日历和计划表等功能升级

BCGControlBar库拥有500多个经过全面设计、测试和充分记录的MFC扩展类。 我们的组件可以轻松地集成到您的应用程序中&#xff0c;并为您节省数百个开发和调试时间。 BCGControlBar专业版 v34.1已正式发布了&#xff0c;这个版本包含了对Windows 10/11字体图标的支持、功能区和…

huggingface 笔记:查看GPU占用情况

0 准备部分 0.1 创建虚拟数据 import numpy as npfrom datasets import Datasetseq_len, dataset_size 512, 512 dummy_data {"input_ids": np.random.randint(100, 30000, (dataset_size, seq_len)),"labels": np.random.randint(0, 1, (dataset_size…

PHP+Lucky+Baby母婴用品网站的设计与实现75554-计算机毕业设计项目选题推荐(附源码)

摘 要 近年来&#xff0c;随着移动互联网的快速发展&#xff0c;电子商务越来越受到网民们的欢迎&#xff0c;电子商务对国家经济的发展也起着越来越重要的作用。简单的流程、便捷可靠的支付方式、快捷畅通的物流快递、安全的信息保护都使得电子商务越来越赢得网民们的青睐。现…

中国低调海外巨头,实力超乎想象!

在全球化的浪潮中&#xff0c;中国公司正以前所未有的速度和规模走向世界。他们或许低调&#xff0c;但却实力非凡&#xff0c;在国际市场上掀起了一股不可小觑的“中国风暴”。今天&#xff0c;就让我们揭开那些在国外牛逼到爆炸的中国公司的神秘面纱&#xff0c;深度解析他们…