libevent入门教程

文章目录

    • 前言
    • libevent-API调用的基本流程
    • 给事件提供缓冲区

前言

本文是libevent的入门文档。本文不涉及,这个库在某些方面的具体使用。

本文内容来自A tiny introduction to asynchronous IO。通过这篇链接,我们可以了解libevent的基本使用。在开始之前,我们需要一些前置准备:

  • TCP客户端和服务端之间的同步通信:chapter05_TCP客户_服务器示例
  • IO复用-select的使用:unix网络编程-select函数
  • 如果知道这两个就更好了:epoll实现Reactor模式、使用asio实现一个单线程异步的socket服务程序

注释:libevent-文档、libevent-API


libevent-API调用的基本流程

在使用API之前,我们需要了解这三部分:

  1. event_base:event_base是一个事件处理的基础结构,它提供了事件循环的基本框架。可以认为它是对select、epoll等函数的封装,以提供跨平台的支持。
  2. event:event是一个表示某种事件(套接字的读写事件,定时器事件等)的结构体或对象。它保存了事件触发时,回调函数的指针,供event_base调用。事件能被触发的前提时,事件被注册到event_base中,参与循环检查。
  3. event loop:运行event_base,直到没有任何注册事件。

总的来说:创建event对象后,需要将其添加到一个event_base实例中,以便在事件发生时被正确处理。event_base提供了方法来添加、删除和处理事件。当事件发生时,event_base负责调用与之相关联的回调函数。下面时一个简单的伪代码示例:

// 创建 event_base 实例
struct event_base *base = event_base_new();// 创建一个读事件
struct event *ev = event_new(base, fd, EV_READ | EV_PERSIST, callback, arg);// 将事件添加到事件循环中
event_add(ev, NULL);// 开始事件循环
event_base_dispatch(base);

具体的API接口使用,见官方文档。

下面我们来看一个提供ROT13功能的服务端程序。

// come from: https://libevent.org/libevent-book/01_intro.html/* For sockaddr_in */
#include <netinet/in.h>
/* For socket functions */
#include <sys/socket.h>
/* For fcntl */
#include <fcntl.h>#include <event2/event.h>#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>#define MAX_LINE 16384void do_read(evutil_socket_t fd, short events, void *arg);
void do_write(evutil_socket_t fd, short events, void *arg);char rot13_char(char c) {/* We don't want to use isalpha here; setting the locale would change* which characters are considered alphabetical. */if ((c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M'))return c + 13;else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z'))return c - 13;elsereturn c;
}struct fd_state {char buffer[MAX_LINE];size_t buffer_used;size_t n_written;size_t write_upto;struct event *read_event;struct event *write_event;
};struct fd_state *alloc_fd_state(struct event_base *base, evutil_socket_t fd) {struct fd_state *state = malloc(sizeof(struct fd_state));if (!state)return NULL;state->read_event = event_new(base, fd, EV_READ | EV_PERSIST, do_read, state);if (!state->read_event) {free(state);return NULL;}state->write_event =event_new(base, fd, EV_WRITE | EV_PERSIST, do_write, state);if (!state->write_event) {event_free(state->read_event);free(state);return NULL;}state->buffer_used = state->n_written = state->write_upto = 0;assert(state->write_event);return state;
}void free_fd_state(struct fd_state *state) {event_free(state->read_event);event_free(state->write_event);free(state);
}void do_read(evutil_socket_t fd, short events, void *arg) {struct fd_state *state = arg;char buf[1024];int i;ssize_t result;while (1) {assert(state->write_event);result = recv(fd, buf, sizeof(buf), 0);if (result <= 0)break;for (i = 0; i < result; ++i) {if (state->buffer_used < sizeof(state->buffer))state->buffer[state->buffer_used++] = rot13_char(buf[i]);if (buf[i] == '\n') {assert(state->write_event);event_add(state->write_event, NULL);state->write_upto = state->buffer_used;}}}if (result == 0) {free_fd_state(state);} else if (result < 0) {if (errno == EAGAIN) // XXXX use evutil macroreturn;perror("recv");free_fd_state(state);}
}void do_write(evutil_socket_t fd, short events, void *arg) {struct fd_state *state = arg;while (state->n_written < state->write_upto) {ssize_t result = send(fd, state->buffer + state->n_written,state->write_upto - state->n_written, 0);if (result < 0) {if (errno == EAGAIN) // XXX use evutil macroreturn;free_fd_state(state);return;}assert(result != 0);state->n_written += result;}if (state->n_written == state->buffer_used)state->n_written = state->write_upto = state->buffer_used = 0;event_del(state->write_event);
}void do_accept(evutil_socket_t listener, short event, void *arg) {struct event_base *base = arg;struct sockaddr_storage ss;socklen_t slen = sizeof(ss);int fd = accept(listener, (struct sockaddr *)&ss, &slen);if (fd < 0) { // XXXX eagain??perror("accept");} else if (fd > FD_SETSIZE) {close(fd); // XXX replace all closes with EVUTIL_CLOSESOCKET */} else {struct fd_state *state;evutil_make_socket_nonblocking(fd);state = alloc_fd_state(base, fd);assert(state); /*XXX err*/assert(state->write_event);event_add(state->read_event, NULL);}
}void run(void) {evutil_socket_t listener;struct sockaddr_in sin;struct event_base *base;struct event *listener_event;base = event_base_new();if (!base)return; /*XXXerr*/sin.sin_family = AF_INET;sin.sin_addr.s_addr = 0;sin.sin_port = htons(40713);listener = socket(AF_INET, SOCK_STREAM, 0);evutil_make_socket_nonblocking(listener);// not work in windows systemevutil_make_listen_socket_reuseable(listener);if (bind(listener, (struct sockaddr *)&sin, sizeof(sin)) < 0) {perror("bind");return;}if (listen(listener, 16) < 0) {perror("listen");return;}listener_event =event_new(base, listener, EV_READ | EV_PERSIST, do_accept, (void *)base);/*XXX check it */event_add(listener_event, NULL);event_base_dispatch(base);
}int main(int c, char **v) {setvbuf(stdout, NULL, _IONBF, 0);run();return 0;
}

给事件提供缓冲区

上面的代码中,我们需要为每个连接显示的分配堆栈作为缓冲区,这是麻烦的。

网络异步编程的通常模式是:

  1. 当连接可读时,将数据读取到缓冲区,然后处理。
  2. 将处理结果写入输出缓冲区,然后将连接设置为可写。尽可能多地写入数据。
  3. 写完之后,关闭连接的可写。(因为套接字的输出缓冲区不满的时候,总是可写的。)

Libevent 提供了一个 它的通用机制:Bufferevents。它包含一个底层传输(如套接字)、读缓冲区和写缓冲区缓冲。从底层IO中读取数据到用户层的缓冲区,然后触发读取回调;将需要发送的数据,放入用户层的缓冲区中,libevent会将数据发送出去。每个Bufferevents都有下面四个watermarks:

  • Read low-water mark: 当输入缓冲区存储的数据,比当前设置的低水位多,则调用读取回调。
  • Read high-water mark:当输入缓冲区的数据,达到当前的高水位,缓冲区不再从底层IO去获取更多数据。等待回调函数消耗些数据,缓冲区才有空间继续存放从IO获取来数据。
  • Write low-water mark:当输出缓冲区存储的数据,比当前设置的低水位多,则调用写回调。
  • Write high-water mark:可以暂时不管。具体见官方文档。

下面使用bufferevent,重写上一节的代码:

/* For sockaddr_in */
#include <netinet/in.h>
/* For socket functions */
#include <sys/socket.h>
/* For fcntl */
#include <fcntl.h>#include <event2/buffer.h>
#include <event2/bufferevent.h>
#include <event2/event.h>#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>#define MAX_LINE 16384void do_read(evutil_socket_t fd, short events, void *arg);
void do_write(evutil_socket_t fd, short events, void *arg);int print_output(struct bufferevent *bev) {struct evbuffer *output = bufferevent_get_output(bev);size_t len = evbuffer_get_length(output);char buff[len + 1];int size = evbuffer_copyout(output, buff, len);if (size < 0) {printf("read fail\n");return -1;}buff[size] = '\0';printf("Output Buffer Contents: %s\n", buff);return 0;
}char rot13_char(char c) {/* We don't want to use isalpha here; setting the locale would change* which characters are considered alphabetical. */if ((c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M'))return c + 13;else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z'))return c - 13;elsereturn c;
}void readcb(struct bufferevent *bev, void *ctx) {struct evbuffer *input, *output;char *line;size_t n;int i;input = bufferevent_get_input(bev);output = bufferevent_get_output(bev);while ((line = evbuffer_readln(input, &n, EVBUFFER_EOL_LF))) {for (i = 0; i < n; ++i)line[i] = rot13_char(line[i]);evbuffer_add(output, line, n);evbuffer_add(output, "\n", 1);free(line);}if (evbuffer_get_length(input) >= MAX_LINE) {/* Too long; just process what there is and go on so that the buffer* doesn't grow infinitely long. */char buf[1024];while (evbuffer_get_length(input)) {int n = evbuffer_remove(input, buf, sizeof(buf));for (i = 0; i < n; ++i)buf[i] = rot13_char(buf[i]);evbuffer_add(output, buf, n);}evbuffer_add(output, "\n", 1);}
}void errorcb(struct bufferevent *bev, short error, void *ctx) {if (error & BEV_EVENT_EOF) {/* connection has been closed, do any clean up here *//* ... */} else if (error & BEV_EVENT_ERROR) {/* check errno to see what error occurred *//* ... */} else if (error & BEV_EVENT_TIMEOUT) {/* must be a timeout event handle, handle it *//* ... */}bufferevent_free(bev);
}void do_accept(evutil_socket_t listener, short event, void *arg) {struct event_base *base = arg;struct sockaddr_storage ss;socklen_t slen = sizeof(ss);int fd = accept(listener, (struct sockaddr *)&ss, &slen);if (fd < 0) {perror("accept");} else if (fd > FD_SETSIZE) {close(fd);} else {struct bufferevent *bev;evutil_make_socket_nonblocking(fd);bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);bufferevent_setcb(bev, readcb, NULL, errorcb, NULL);bufferevent_setwatermark(bev, EV_READ, 0, MAX_LINE);bufferevent_enable(bev, EV_READ | EV_WRITE);}
}void run(void) {evutil_socket_t listener;struct sockaddr_in sin;struct event_base *base;struct event *listener_event;base = event_base_new();if (!base)return; /*XXXerr*/sin.sin_family = AF_INET;sin.sin_addr.s_addr = 0;sin.sin_port = htons(40713);listener = socket(AF_INET, SOCK_STREAM, 0);evutil_make_socket_nonblocking(listener);#ifndef WIN32{int one = 1;setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));}
#endifif (bind(listener, (struct sockaddr *)&sin, sizeof(sin)) < 0) {perror("bind");return;}if (listen(listener, 16) < 0) {perror("listen");return;}listener_event =event_new(base, listener, EV_READ | EV_PERSIST, do_accept, (void *)base);/*XXX check it */event_add(listener_event, NULL);event_base_dispatch(base);
}int main(int c, char **v) {setvbuf(stdout, NULL, _IONBF, 0);run();return 0;
}

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

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

相关文章

记录一下github深度学习的错误

1.[visdom]无法正常启动服务问题解决 在Anaconda命令窗口中&#xff1a; 使用python -m visdom.server启动visdom服务时&#xff0c;卡在&#xff1a; Checking for scripts. Downloading scripts, this may take a little while 无法下载和启动服务。 ERROR&#xff1a;由…

设计模式-策略(Strategy)模式

又被称为政策&#xff08;方针&#xff09;模式策略模式(Strategy Design Pattern)&#xff1a;封装可以互换的行为&#xff0c;并使用委托来决定要使用哪一个策略模式是一种行为设计模式&#xff0c;它能让你定义一系列算法&#xff0c;并将每种算法分别放入独立的类中&#x…

select、poll、epoll 区别有哪些

文章目录 select、poll、epoll 区别有哪些&#xff1f;select&#xff1a;poll&#xff1a;epoll&#xff1a; select、poll、epoll 区别有哪些&#xff1f; select&#xff1a; 它仅仅知道了&#xff0c;有 I/O 事件发生了&#xff0c;却并不知道是哪那几个流&#xff08;可…

[MySQL]数据库概述

目录 1.什么是数据库 2.数据库分类 2.1关系型数据库 2.2非关系型数据库 1.什么是数据库 我们知道&#xff0c;存储数据可以使用文件来存储。那么为什么我们还要大费周章的去设计和使用数据库呢&#xff1f; 因为文件保存数据有以下几个缺点&#xff1a; 1.文件的安全性不…

浅谈MapReduce

MapReduce是一个抽象的分布式计算模型&#xff0c;主要对键值对进行运算处理。用户需要提供两个自定义函数&#xff1a; map&#xff1a;用于接受输入&#xff0c;并生成中间键值对。reduce&#xff1a;接受map输出的中间键值对集合&#xff0c;进行sorting后进行合并和数据规…

clickhouse函数记录

日期函数 SELECT formatDateTime(create_time,%Y-%m-%d) AS time FROM xx.xx;

xtu oj 1171 Coins

题目描述 一个均质硬币抛n次&#xff0c;求不存在连续2次为正面的方案数。 输入 每行一个正整数n&#xff0c;n≤40。如果n为0&#xff0c;表示输入结束&#xff0c;不需要处理。 输出 每行输出一个结果&#xff0c;为一个整数。 样例输入 1 2 3 0样例输出 2 3 5 AC代…

2023-12-16 课后练习(复习+结构体练习)

题目&#xff1a;分式运算&#xff0c;1-1/21/3-1/4…1/99-1/100 代码&#xff1a; #include<stdio.h> int main() {int i 1;//项数&#xff0c;初始值为1double deno 2;//分母&#xff0c;初始值为2&#xff0c;从第二项开始double n 1;//项的数值&#xff0c;初始…

安路IP核应用举例(OSC、UART)

1.OSC(内部振荡器) 按照Project->New Project顺序新建工程后&#xff0c;后按照Tools->IP Generator顺序&#xff0c;创建IP核&#xff0c;如下图&#xff1a; 安路FPGA的内置OSC振荡模块频率可选30MHz、60MHz。 可选Verilog或VHDL语言。 如图&#xff0c;生成的.v文件只…

软件测试计划文档

软件测试文档 【B站最系统的软件测试教程】阿里兼字节大佬200小时讲完的测试教程&#xff0c;全程干货无废话&#xff01;学完即可就业&#xff0c;别在盲目自学&#xff01;&#xff01;&#xff01; 1.引言 1.1编写目的 为此次飞机大战软件提供完善的测试指导&#xff0c;组…

【Linux】内核结构

一、Linux内核结构介绍 Linux内核结构框图 二、图解Linux系统架构 三、驱动认知 1、为什么要学习写驱动2、文件名与设备号3、open函数打通上层到底层硬件的详细过程 四、Shell Shell脚本 一、Linux内核结构介绍 Linux 内核是操作系统的核心部分&#xff0c;它负责管理系…

“Java 已死、前端已凉”?技术变革与编程语言前景:Java和前端的探讨

前端已死话题概论 本文讨论了近期IT圈中流传的“Java 已死、前端已凉”言论。我们审视了这些言论的真实性&#xff0c;并深入探讨了技术行业的演变和新兴技术的出现对编程语言和前端开发的影响。通过分析历史发展、当前趋势和未来展望&#xff0c;我们提供了对这些话题更深层次…

HBuilderX 配置 夜神模拟器 详细图文教程

在电脑端查看App的效果&#xff0c;不用真机调试&#xff0c;下载一个模拟器就可以了 --- Nox Player&#xff0c;夜神模拟器&#xff0c;是一款 Android 模拟器。他的使用非常安全&#xff0c;最重要的是完全免费。 一. 安装模拟器 官网地址&#xff1a; (yeshen.com) 二.配…

探索性能测试的奥秘:流程与工具大揭秘!

一、性能测试 性能测试是通过自动化的测试工具模拟多种正常、峰值以及异常负载条件来对系统的各项性能指标进行测试。 1.1 类别 性能测试包括负载测试、压力测试、基准测试等。 1.1.1 负载测试 通过测试系统在资源超负荷情况下的表现&#xff0c;以发现设计上的错误或验证…

【MYSQL】事务隔离级别、脏读、不可重复读、幻读

文章目录 介绍演示脏读不可重复读可重复读幻读 不可重复读和幻读的区别 参考 作者 Guide: 事务隔离级别 美团技术团队&#xff1a; Innodb中的事务隔离级别和锁的关系 介绍 SQL 标准定义了四个隔离级别&#xff1a; READ-UNCOMMITTED(读取未提交) &#xff1a;最低的隔离级别…

论文阅读——Semantic-SAM

Semantic-SAM可以做什么&#xff1a; 整合了七个数据集&#xff1a; 一般的分割数据集&#xff0c;目标级别分割数据集&#xff1a;MSCOCO, Objects365, ADE20k 部分分割数据集&#xff1a;PASCAL Part, PACO, PartImagenet, and SA-1B The datasets are SA-1B, COCO panopt…

java简易制作-王者荣耀游戏

一.准备工作 首先创建一个新的Java项目命名为“王者荣耀”&#xff0c;并在src下创建两个包分别命名为“com.sxt"、”com.stx.beast",在相应的包中创建所需的类。 创建一个名为“img”的文件夹来储存所需的图片素材。 二.代码呈现 package com.sxt; import javax…

【设计模式--行为型--观察者模式】

设计模式--行为型--观察者模式 观察者模式定义结构案例优缺点使用场景JDK中提供的实现例&#xff1a;警察抓小偷 观察者模式 定义 又被成为发布订阅模式&#xff0c;它定义了一种一对多的依赖关系&#xff0c;让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生…

apollo7.0——规划代码解析

Planning模块的入口为"planning_component.h"和"planning_component.cc"两个文件&#xff0c;实现的功能如下&#xff1a; bool PlanningComponent::Init() {injector_ std::make_shared<DependencyInjector>();/*** modules/common/configs/confi…

INFINI Labs 产品更新 | Easysearch 新增快照搜索功能,Console 支持 OpenSearch 存储

INFINI Labs 产品又更新啦~&#xff0c;包括 Easysearch v1.7.0、Console v1.13.0。本次各产品更新了 Easysearch 快照搜索功能&#xff1b;Console 支持 OpenSearch 集群存储系统数据、优化了初始化安装向导流程等。 以下是本次更新的详细说明。 INFINI Easysearch v1.7.0 …