当前位置:首页 > 技术文章 > 正文内容

「linux」详解libevent网络库---框架的搭建

arlanguage3个月前 (01-31)技术文章29

libevent概述

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

初识libevent

只要是学习编程似乎都逃离不了“hello word”定律!(嗯…真香!)编译安装libevent源码之后,进入文件sample cd /sample接下来,我们可以看到libevent官方为我们提供的demo

vim打开hello-world.c文件查看示例代码

发现监听端口号为:9995好,接下来我们开始libevent的奇妙之旅,使用xshell启动两个本地连接,

  1. 服务端:在上述路径执行 ./hello-world
  2. 客户端:采用nc进行访问 nc 127.0.0.1 9995 9995为上述代码中指定的端口号

每有客户端注册时 server端将打印 flushed answer,同时,client端打印Hello, World!

需要C/C++ Linux服务器架构师学习资料私信“资料”(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享

框架学习-- event_base 重中之重

源码中有这样一句话:The event_base lies at the center of Libevent; every application will have one.

夸张的理解为:libevent的世界中,event_base作为万物起源

使用 libevent 函数之前需要分配一个或者多个 event_base 结构体。每个event_base
结构体持有一个事件集合,可以检测以 确定哪个事件是激活的。(相当于epoll红黑树的树根

从思想上出发

我们可以把libevent项目想象为造火箭的过程,我们都只是螺丝工,那么造火箭需要做什么?

1. 拿出火箭壳(创建框架)
2. 造螺丝 (创建事件)
3. 拧螺丝 (添加事件)
4. 造火箭(事件循环)

一:掏出火箭壳 —>event_base()创建与释放

我们开始第一步:创建一个event_base

// 创建event_base
struct event_base* event_base_new(void)

当然,作为一个优秀的c语言程序员(咳,我还是个菜),要在创建的同时考虑资源释放的问题,在程序的最后我们需要 event_base_free 进行释放(但我们不得不提前考虑)

// 释放event_base_free
event_base_free(struct event_base* base);
12

二:造螺丝 —>event_new()创建与释放

注:创建事件:(只针对不带缓冲区event事件进行讲解)

// 创建新事件
	struct event *event_new(
		struct event_base *base, 
		evutil_socket_t fd, - // 文件描述符 - int  **底层是对epollin与epollout的封装**
		short what, 
		event_callback_fn cb, // 事件的处理回调函数
		void *arg //回调函数传参
); 
// 事件的处理回调函数
	typedef void (*event_callback_fn)(evutil_socket_t, short, void *); 
//	short what
	#define  EV_TIMEOUT         0x01    // 已淘汰(忽略)
	#define  EV_READ            0x02
	#define  EV_WRITE           0x04
	#define  EV_SIGNAL          0x08    //libevent封装了信号相关的操作 SIGNAL
	#define  EV_PERSIST         0x10    // 持续触发
	#define  EV_ET              0x20    // 边沿模式

在程序的最后我们需要 event_free 进行释放(但我们不得不提前考虑)

// 创建event_free
	void event_free(struct event *event); 

调用event_new()函数之后, 新事件处于已初始化和非未决状态 (翻译:螺丝造好了,还没拧到火箭时的状态)

三:拧螺丝 —>event_add()相关函数

创建事件之后,在将其添加到 event_base 之前实际上是不能对其做任何操作的。使用event_add()将事件添加到event_base。 (将螺丝拧上去)非未决事件 -> 未决事件.

// event_add
	int event_add(
				struct event *ev, 
				const struct timeval *tv
				); 

函数调用成功返回0, 失败返回-1

设置非未决(作为了解实际中少用)未决事件 -> 非未决事件.

//对已经初始化的事件调用 event_del()将使其成为非未决和非激活的。如果事件不是未决的或者激活的,调用将没有效果。
		int event_del(struct event *ev); 
//成功时函数返回 0,失败时返回-1。

有关于未决态和非未决态的理解:

1. 非未决: 没有资格被处理

2. 未决: 有资格被处理但是还没有处理

四:一节一节造火箭 —>event_base_dispatch()相关函数

最后,我们只需将添加 事件循环使用event_base_dispatch()函数

// event_base_dispatch(简化版event_base_loop())
	int event_base_dispatch(struct event_base* base);
	//等同于没有设置标志的 event_base_loop ( )
	//将一直运行,直到没有已经注册的事件了,或者调用 了event_base_loopbreak()或者 event_base_loopexit()为止

关于event_base_loop()函数

// event_base_loop()
	int event_base_loop(struct event_base *base, int flags);
//正常退出返回0, 失败返回-1

//flages
#define EVLOOP_ONCE                        0x01
		//事件只会被触发一次
		//事件没有被触发, 阻塞等
#define EVLOOP_NONBLOCK                    0x02
		//非阻塞 等方式去做事件检测
		//不关心事件是否被触发了
#define EVLOOP_NO_EXIT_ON_EMPTY  		   0x04
		//没有事件的时候, 也不退出轮询检测

关于退出事件循环函数event_base_loopexit()与event_base_loopbreak():

执行当前后退出 event_base_loopexit()

//如果 event_base 当前正在执行激活事件的回调 ,它将在执行完当前正在处理的事件后立即退出
	 int event_base_loopexit(
				struct event_base *base,
				const struct timeval *tv
				);
//参数struct timeval *tv
			struct timeval {
			       long   tv_sec;                    
				   long   tv_usec;            
			};

立即退出循环 event_base_loopbreak()

//让event_base 立即退出循环
		 int event_base_loopbreak(struct event_base *base);
		 
//返回值: 成功 0, 失败 -1

Demo

最后举个栗子:注:用不带缓冲区操作时的写法:采用fifo通讯的方式

//write_fifo.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#include <event2/event.h>

// 回调函数
void write_cb(evutil_socket_t fd, short what, void *arg)
{
    // write管道
    char buf[1024] = {0};
    static int num = 666;
    sprintf(buf, "hello, world == %d\n", num);
    write(fd, buf, strlen(buf)+1);
}


int main(int argc, const char* argv[])
{
    // open file
    int fd = open("myfifo", O_WRONLY | O_NONBLOCK);
    if(fd == -1)
    {
        perror("open error");
        exit(1);
    }

    // 写管道
    struct event_base* base = NULL;
    base = event_base_new();

    // 创建事件
    struct event* ev = NULL;
    // 检测的写缓冲区是否有空间写
    ev = event_new(base, fd, EV_WRITE , write_cb, NULL);

    // 添加事件
    event_add(ev, NULL);

    // 事件循环
    event_base_dispatch(base);

    // 释放资源
    event_free(ev);
    event_base_free(base);
    close(fd);
    
    return 0;
}
//read_fifo
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#include <event2/event.h>

// 读取回调函数  
void read_cb(evutil_socket_t fd, short what, void *arg)
{
    // 读管道
    char buf[1024] = {0};
    int len = read(fd, buf, sizeof(buf));
    printf("data len = %d, buf = %s\n", len, buf);
    printf("read event: %s", what & EV_READ ? "Yes" : "No");//对what类型对判断
}

int main(int argc, const char* argv[])
{
    unlink("myfifo");
    //创建有名管道
    mkfifo("myfifo", 0664);
    
    // open file
    int fd = open("myfifo", O_RDONLY | O_NONBLOCK);
    if(fd == -1)
    {
        perror("open error");
        exit(1);
    }

    // 读管道
    struct event_base* base = NULL;
    base = event_base_new();

    // 创建事件
    struct event* ev = NULL;
    ev = event_new(base, fd, EV_READ | EV_PERSIST, read_cb, NULL);

    // 添加事件
    event_add(ev, NULL);

    // 事件循环
    event_base_dispatch(base);

    // 释放资源
    event_free(ev);
    event_base_free(base);
    close(fd);
    
    return 0;
}

使用gcc对文件编译 -levevt引用libevent库

gcc read_fifo.c -o read -levent

gcc write_fifo.c -o write -levent

./read ./write 依次执行即可

扫描二维码推送至手机访问。

版权声明:本文由AR编程网发布,如需转载请注明出处。

本文链接:http://www.arlanguage.com/post/1818.html

标签: nginx框架
分享给朋友:

“「linux」详解libevent网络库---框架的搭建” 的相关文章

解决php因为输出内容太短无法流式(Stream)输出问题

实测得知,如果用PHP做流式输出每次echo的内容太短的话,就没有流式的效果了,而是会等输出的长度达到一定时前端才能拿到结果,如下:这是我这边的测试结果,可见,这里仅接收到了两次流式输出,但是我在PHP里是输出了10次,从而证明了每次流式的内容达到一定长度时才能有流式得到效果,这个长度到底是多少,我...

我采访了一位 Pornhub 工程师,聊了这些纯纯的话题

成人网站在推动 Web 发展方面所起到的作用无可辩驳。从突破浏览器的视频能力限制,到利用 WebSocket 推送广告(防止被广告拦截器拦截),你必须不断想出各种聪明的办法,让自己处在 Web 技术创新的最前沿。最近,我有幸采访了大型成人网站 Pornhub 的一位 Web 开发工程师,了解了相关的...

网站nginx配置限制单个IP访问频率,预防DDOS恶意攻击

一、简介对于网站来说,尤其是流量较大出名的网站,经常遇到攻击,如DDOS攻击等,虽然有些第三方,如Cloudflare可以挡,但对于动态网站PHP来说,只能挡一部分。这时候需要对于单个IP恶意攻击做出限流。nginx的两个模块可以限流。nginx两个限流模块:连接频率限制,ngx_http_lim...

K8S灰度环境频繁出现502 k8s ingress 灰度发布

公司一个需求版本开发、测试结束,升级到灰度环境之后频繁出现502公司使用的是php + nginx 所以分析也是按照这个方向来分析的。nginx报错:NGINX ERROR 10.9.95.75 !!!2020/07/21 17:05:12 [error] 7#7: *38808 recv() fa...

Nginx 最全操作——nginx配置(2)

nginx 配置基本结构main # 全局配置,对全局生效 ├── events # 配置影响 nginx 服务器或与用户的网络连接 ├── http # 配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置 │ ├── upstream # 配置后端服务器具体地址,负...

Nginx从入门到放弃03-Nginx调优

一、调优的必要性在聊调优之前,我们先要知道为何调优,业务运行和调优的关系。笔者把自己总结的文档分为几遍,合集在 https://g.xgss.net/nginx/业务运行:线上业务正常运行,承载了公司业务。 监控业务:通过监控业务对线上业务进行监控,及时发现问题。 优化业务:通过监控分析,发现业务问...