详解libevent网络库(一)---框架的搭建
使用libevent实现进程间通讯
- libevent概述
- 起源
- 获取源码
- 初识libevent
- 框架学习-- event_base 重中之重
- 从思想上出发
- 一:掏出火箭壳 --->event_base()创建与释放
- 二:造螺丝 --->event_new()创建与释放
- 三:拧螺丝 --->event_add()相关函数
- 四:一节一节造火箭 --->event_base_dispatch()相关函数
- Demo
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等等。
链接-来自百度百科: https://baike.baidu.com/item/libevent/.
起源
起初对libevent、libev、libuv等网络库有所耳目,未能拜读,最近在开发公司项目的时,发现底层组件之间交互采用了libevent进行编写,遂对其学习研究。公司中采用libevent 2.0之前的版本进行研发,对比发现,旧版本中很多函数过于冗杂,现对libevent 2.0之后的新版本进行学习研究:
在Libevent2.0之前的版本中,没有event_assign或者event_new函数,而只有event_set函数,该函数返回的event与“当前”base相关联。如果有多个event_base,则还需要调用event_base_set函数指明event与哪个base相关联
获取源码
Github: https://github.com/libevent/libevent
初识libevent
只要是学习编程似乎都逃离不了“hello word”定律!(嗯…真香!)
编译安装libevent源码之后,进入文件sample cd /sample
接下来,我们可以看到libevent官方为我们提供的demo,
vim打开hello-world.c文件查看示例代码。
发现监听端口号为:9995
好,接下来我们开始libevent的奇妙之旅,
使用xshell启动两个本地连接,
- 服务端:在上述路径执行 ./hello-world
- 客户端:采用nc进行访问 nc 127.0.0.1 9995 9995为上述代码中指定的端口号
每有客户端注册时 server端将打印 flushed answer,同时,client端打印Hello, World!
框架学习-- 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);
二:造螺丝 —>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_freevoid event_free(struct event *event);
调用event_new()函数之后, 新事件处于已初始化和非未决状态 (翻译:螺丝造好了,还没拧到火箭时的状态)
三:拧螺丝 —>event_add()相关函数
创建事件之后,在将其添加到 event_base 之前实际上是不能对其做任何操作的。
使用event_add()将事件添加到event_base。 (将螺丝拧上去)
非未决事件 -> 未决事件.
// event_addint event_add(struct event *ev, const struct timeval *tv);
- tv:参数 | Value |
---|---|
NULL: | 事件被触发, 对应的回调被调用 |
tv = {0, n} | 设置的时间,在改时间段内检测的事件没被触发, 时间到达之后, 回调函数还是会被调用 |
函数调用成功返回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 *tvstruct 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 fileint 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 fileint 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 依次执行即可
下一篇将讲述 带数据缓冲区 - Bufferevent …利用Bufferevent 实现网络通讯
详解libevent网络库(一)---框架的搭建相关推荐
- 详解Libevent网络库
项目中要用到libevent,所以就自学了libevent,参考资料为张亮的<libevent源码深度剖析>和<linux高性能服务器编程> Libevent简介 Libeve ...
- 详解一个Python库,用于构建精美数据可视化web app,练习做个垃圾分类app
点击上方"Python爬虫与数据挖掘",进行关注 回复"书籍"即可获赠Python从入门到进阶共10本电子书 今 日 鸡 汤 醉卧沙场君莫笑,古来征战几人回? ...
- Gcc详解以及静态库、动态库生成
[转] Gcc详解以及静态库.动态库生成 http://www.360doc.com/content/10/0619/14/1795182_33985297.shtml 1.gcc包含的c/c++编译 ...
- Windows 网络服务架构系列课程详解(一) ----DHCP服务器的搭建与配置
Windows 网络服务架构系列课程详解(一) ---------DHCP服务器的搭建与配置 实验背景: 企业网络环境中在没有配置DHCP服务器时,经常会遇到这样的情况,用户不懂怎么去配置IP地址 ...
- [Pytorch系列-61]:循环神经网络 - 中文新闻文本分类详解-3-CNN网络训练与评估代码详解
作者主页(文火冰糖的硅基工坊):文火冰糖(王文兵)的博客_文火冰糖的硅基工坊_CSDN博客 本文网址:https://blog.csdn.net/HiWangWenBing/article/detai ...
- pillow属于python标准库吗_详解Python图像处理库Pillow常用使用方法
PIL(Python Image Library)是python的第三方图像处理库,但是由于其强大的功能与众多的使用人数,几乎已经被认为是python官方图像处理库了. 其官方主页为:PIL. PIL ...
- Vue.js - Font Awesome字体图标的使用详解(vue-fontawesome库)
Vue.js - Font Awesome字体图标的使用详解(vue-fontawesome库) Font Awesome 是一个十分优秀的第三方图标库,我之前也写过文章介绍如何在 html 页面中使 ...
- 十二、详解计算网络中的流量控制和差错控制、HDLC
十二.详解计算网络中的流量控制和差错控制 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 例如:第一章 Python 机器学习入门之pandas的使用 提示:写完文章后,目录可以自动 ...
- 【React】 详解下一代开源混合应用框架Reapp
详解下一代开源混合应用框架Reapp reapp官网 转载于:https://www.cnblogs.com/dongdong230/p/4314978.html
最新文章
- ultraMaskedEdit使用心得
- Hyperledger Fabric 1.0 实战开发系列 第⑤课 fabric 证书解析
- java出现errors是什么错误_java中错误(error)和异常(exception)有什么主要区别?
- 收到postmaster附件被删除的退信
- Java的二十三种设计模式(原型模式(Prototype))
- 三天100元从零开始搭建Hadoop集群
- Tomcat服务器 Servlet
- 【hadoop】java.io.IOException: No FileSystem for scheme: hdfs
- Detour hook库x64编译
- 西方新冠疫苗有效率的数据
- 扇贝有道180919每日一句
- java整人的代码_「vbs代码」vbs表白代码+整人代码,抖音vbscript表白代码 - seo实验室...
- Spring中bean的生命周期
- html5视频自动轮播,HTML5教程 可自动轮播的旋转木马插件
- STM32 DCMI OV9655 直接在LCD显示
- 台式计算机摄像头怎么打开,如何打开摄像头,教您Win7摄像头怎么打开
- SIM900A是什么
- Gatekeeper代码导读
- 3dmax 2016 2015 2014 2013 vary渲染 视频教程 从入门到精通
- 《C语言程序设计教程》(一)
热门文章
- HTML常用标签之段落标签
- 普通发票模板 html css写的
- 2023,“蔚小理”真的经不起更多“事故”了
- [Flash]Loading制作
- android仿腾讯体育app,腾讯体育app
- 嵌入式实时软件平台TOPPERS/ASP简介
- R型变压器空载电压和负载电压区别!
- 关于apriori算法中置信度、支持度怎么理解的问题
- 计算机桌面怎么截图快捷键,电脑截图的快捷键是什么_屏幕截图快捷键_怎么截图快捷键-太平洋IT百科手机版...
- 老男孩39期决心书——刘浩海