libev学习笔记
转
libev的使用——结合Socket编程
作者:cxy450019566
之前自己学过一些libev编程的基础,这次写压测刚好用上了,才算真正动手写了些东西,在这里做一些总结。写这篇文章是为了用浅显易懂的语言帮助大家做一个入门,我自己也是入门程序媛一只,所以有理解错误的地方欢迎指出。
首先推荐几个我认为学习libev比较好的blog,最后一个地址是官方文档,给了我很多帮助:
http://vimersu.win/blog/2014/03/06/libev-study/
http://www.cnblogs.com/dirlt/archive/2011/09/07/2169344.html#sec-1
http://blog.csdn.net/woxiaozhi/article/details/16963641
http://www.cnblogs.com/wunaozai/p/3950249.html
一.libev原理
之前简单写过一篇libev的学习:http://blog.csdn.net/cxy450019566/article/details/52416349
有时间可以看看,没时间直接看这一篇文章。
需要理解的第一句话:Libev的核心是一个event loop。一个event loop,通俗讲就是一个不停在循环运行的事件。
需要理解的第二句话:Libev通过分配和注册watcher对不同类型的事件进行监听,当监听被触发时,进行相应的操作。在一个event loop中,我们可以设定对很多libev支持的事件(见上一篇博客)的监听watcher,这些事件的监听是异步进行的,触发任意一个监听的事件,都可以根据我们的设定进行某些操作。
理解了这两句话,你就可以理解libev的作用,以及怎样来使用libev。
二.通过简单示例了解libev基本用法
我们从官方示例,来学习libev的基本用法。
// libev需要的头文件
#include <ev.h>
#include <stdio.h> // 建立我们刚刚说的需要监听的事件,这些事件类型是libev提供的
// with the name ev_TYPE
//ev_io和ev_timer最为常用,ev_io为监听控制台输入,ev_timer为时间事件
ev_io stdin_watcher;
ev_timer timeout_watcher; // 以下为自定义的回调函数,当触发监听事件时,调用执行对应的函数 // ev_io事件的回调函数,当有输入流stdin时,调用函数
static void stdin_cb (EV_P_ ev_io *w, int revents)
{
puts ("stdin ready");
//对ev_io事件的监控不会自动停止,需要手动在需要的时候停止
ev_io_stop (EV_A_ w); //整体的loop事件在所有监控停止时停止,也可以手动关闭全部的ev_run
ev_break (EV_A_ EVBREAK_ALL);
} // 时间事件的自定义回调函数,可定时触发
static void timeout_cb (EV_P_ ev_timer *w, intrevents)
{
puts ("timeout");
//关闭最早的一个还在运行的ev_run
ev_break (EV_A_ EVBREAK_ONE);
} int main (void)
{
//定义默认的 event loop,它就像一个大容器,可以装载着很多事件不停运行
struct ev_loop *loop = EV_DEFAULT; // 初始化ev_io事件监控,设置它的回调函数,和stdin
ev_io_init (&stdin_watcher, stdin_cb,/*STDIN_FILENO*/ 0, EV_READ);
//将ev_io事件放到event loop里面运行
ev_io_start (loop, &stdin_watcher); // 初始化ev_timer事件监控,设置它的回调函数,间隔时间,是否重复
ev_timer_init (&timeout_watcher, timeout_cb, 5.5,0.);
//将ev_timer事件放到event loop里面运行
ev_timer_start (loop, &timeout_watcher); // 将我们的大容器event loop整体运行起来
ev_run (loop, 0); // ev_run运行结束之后,才会运行到这里
return 0;
}
通过上面的例子,就可以知道libev使用的思路:
首先,定义一个eventloop大容器 struct ev_loop,和想要的监控事件ev_XXX。
其次,初始化想要监控的事件,设置好回调函数和相应的参数ev_XXX_init 。
接下来,让想要监控的事件都投身到大容器中ev_XXX_start。
最后,让大容器带着小容器一起运行起来 ev_run 。
三.常用函数详解
1.event loop 相关
(1)从创建说起:
我们默认使用EV_DEFAULT类型的loop,使用一下语句来创建:struct ev_loop *loop = EV_DEFAULT;
EV_DEFAULT宏是以下指令:
ev_default_loop(EVBACKEND_POLL | EVBACKEND_SELECT | EVFLAG_NOENV);
返回一个最基础的ev_loop,并自动完成它的初始化,注意,如果程序中已经执行过该创建,将直接返回之前的创建。除此之外,更多自定义loop,可以使用该函数:struct ev_loop*ev_loop_new (unsigned int flags)。
(2)让ev_loop运行起来
使用函数ev_run(loop, int flags)。
这里解释一下flags的作用,用于设置ev_loop的运行方式:
通常设置为0,表示该ev_loop在所有watcher结束后停止,也可以手动break,官方鼓励手动break。
除了0之外,还有一些选择,如EVRUN_NOWAIT、EVRUN_ONCE。具体请看官方文档。
ev_run函数的源码说明。推荐博文:http://www.cnblogs.com/xiangshancuizhu/archive/2013/08/10/3250558.html
(3)ev_loop的停止
前面已经说过,在flags设置为0的情况下,停止主要靠全部watcher停止或者手动break。
手动break用以下函数:ev_break (loop,how)
其中,how代表停止的方式:
EVBREAK_ONE:停止最久远的那个ev_run
EVBREAK_ALL:停止所有的ev_run
2.ev_TYPE公共基础
(1)watcher对应的几种状态
initialiased:调用init函数初始化
active:调用start进行注册
pending:已经触发事件但是没有处理
inactive:调用stop注销。这个状态等同于initialised这个状态。
(2)ev_TYPE对应不同类型的时间监控,共有的标准化函数主要如下:
typedef void (*)(struct ev_loop *loop, ev_TYPE*watcher, int revents) callback; // 回调函数都是这种类型
ev_init (ev_TYPE *watcher, callback); // 初始化watcher
ev_TYPE_set (ev_TYPE *watcher, [args]); // 设置watcher
ev_TYPE_init (ev_TYPE *watcher, callback, [args]); // 这个函数综合了前两个函数功能
ev_TYPE_start (loop, ev_TYPE *watcher); // 在ev_loop中注册watcher
ev_TYPE_stop (loop, ev_TYPE *watcher); // 在ev_loop中注销watcher
ev_set_priority (ev_TYPE *watcher, int priority); // 设置watcher优先级,值域为[-2,2],大而优先
ev_feed_event (loop, ev_TYPE *watcher, int revents);// 这个做跨线程通知非常有用,相当于触发了某个事件。
bool ev_is_active (ev_TYPE *watcher); // watcher是否active.
bool ev_is_pending (ev_TYPE *watcher); // watcher是否pending.
int ev_clear_pending (loop, ev_TYPE *watcher); // 清除watcher pending状态并且返回事件
3.ev_io相关
(1)ev_io结构
ev_io用来监听io事件,当有标准输入或输出时,则会触发事件,执行回调函数。
typedef structev_io
{
EV_WATCHER_LIST (ev_io)
int fd; /* 所监听io事件的文件描述符(file descriptor,非负数) */
int events; /*所监听的事件,包括EV_READ, EV_WRITE 或 EV_READ | EV_WRITE */
} ev_io;
(2)初始化
两种方式:
方式一:ev_init(ev_TYPE *watcher, callback) ,ev_io_set(ev_io *ev, intfd, int events)
方式二:ev_io_init(ev_io*ev, callback,int fd, int events)
即需要设置监听watcher,回调函数callback,文件描述符fd,监听的事件events。
(3)运行和停止
ev_io_start(EV_P_ev_io * w):绑定到ev_loop上
ev_io_stop(EV_P_ev_io * w):从ev_loop上撤离
4.ev_timer相关
(1)ev_timer结构
ev_timer是一个相对时间的定时器,会在给定的时间点触发事件,还可以在固定的时间间隔之后再次触发超时事件。
typedef structev_timer
{
int active;
int pending;
int priority; /*优先级 */
void *data; /* 参数*/
void (*cb)(struct ev_loop *loop, struct ev_timer *w,int revents); /*回调函数 */
ev_tstamp at; /* 定时器时间,单位为s*/
ev_tstamp repeat; /*是否重复 */
} ev_timer;
(2)初始化
同样两种方式:
方式一:ev_init(ev_TYPE *watcher, callback) ,ev_timer_set (ev_timer *,ev_tstamp after, ev_tstamp repeat)
方式二:ev_timer_init(ev_timer *, callback, ev_tstamp after, ev_tstamp repeat)
即需要设置监听watcher,回调函数callback,定时器时间after,是否重复repeat。
其中,repeat设置为0表示定时器只触发一次,设置为正整数则间隔after秒一直不断触发。
(3)运行和停止
ev_timer_start(EV_P_ev_io * w):绑定到ev_loop上
ev_timer_stop(EV_P_ev_io * w):从ev_loop上撤离
(4)其它函数
ev_timer_again(loop, ev_timer *) :重启,对于不同状态的timer效果不同。
ev_tstampev_timer_remaining (loop, ev_timer *):返回现在到下一次触发定时器之间的时间。
四.在Socket编程的应用实例
libev用在Socket编程上有几个好处,用ev_timer可以控制发送频率,用ev_io可以方便地异步进行包的接收。下面的实例就实现的这个流程,直接用代码解释了:
double time_span = 1.0; //发送消息的时间间隔
int test_num = 5; //发送消息的次数
int send_num = 0; //已发送消息的次数
int rev_num = 0; //已收到回复的次数
int fd = -1; //文件描述符 struct ev_loop *loop = EV_DEFAULT;
ev_io io_watcher;
ev_timer timer_watcher; //ev_io的回调函数,用于异步接受回复
void io_action(struct ev_loop *main_loop,ev_io*io_w,int e)
{
char buf[4096];
char *ptr = (char *)buf;
int ret = 0;
//接收消息
ret = ::recv(fd, ptr, 4096, 0); std::cout << buf << "\n"; //接收到所有消息,停止ev_io事件
if(rev_num >= test_num){
ev_io_stop(main_loop,io_w);
}
} //ev_timer的回调函数,用于定时发送消息
void timer_action(struct ev_loop *main_loop,ev_timer*time_w,int e)
{
int ret;
std::string msg_send = "send msg";
//发送消息
ret = ::send(fd, msg_send.c_str(),strlen(msg_send.c_str()), 0);
std::cout << msg_send << "\n"; //发送足够数量的消息后,停止事件
if(send_num >= test_num){
ev_timer_stop(main_loop,time_w);
}
} int main(int argc, char *argv[])
{
fd = socket(AF_INET, SOCK_STREAM, 0);
ret = ::connect(fd, (struct sockaddr *)&addr,sizeof(addr)); ev_init(&io_watcher,io_action);
ev_io_set(&io_watcher,fd,EV_READ); ev_init(&timer_watcher,timer_action);
ev_timer_set(&timer_watcher,time_span,1); ev_io_start(loop,&io_watcher);
ev_timer_start(loop,&timer_watcher); ev_run(loop,0);
return 0;
}
---恢复内容结束---
libev学习笔记的更多相关文章
- Libev学习笔记4
这一节首先分析Libev的定时器部分,然后分析signal部分. 对定时器的使用主要有两个函数: ev_timer_init (&timeout_watcher, timeout_cb, .) ...
- Libev学习笔记3
设置完需要监听的事件之后,就开始event loop了.在Libev中,该工作由ev_run函数完成.它的大致流程如下: int ev_run (EV_P_ int flags) { do { /* ...
- Libev学习笔记2
这一节根据官方文档给出的简单示例,深入代码内部,了解其实现机制.示例代码如下: int main (void) { struct ev_loop *loop = EV_DEFAULT; ev_io_i ...
- Libev学习笔记1
和Libevent相似,Libev是一个高性事件驱动框架,据说性能比Libevent要高,bug比Libevent要少.Libev只是一个事件驱动框架,不是网络库,因为它的内部并没有任何socket编 ...
- libev 学习使用
libev 简单的I/O库. a high performance full featured event loop written in c libev 的大小也比 libevent 小得多并且自 ...
- js学习笔记:webpack基础入门(一)
之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...
- PHP-自定义模板-学习笔记
1. 开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2. 整体架构图 ...
- PHP-会员登录与注册例子解析-学习笔记
1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...
- 2014年暑假c#学习笔记目录
2014年暑假c#学习笔记 一.C#编程基础 1. c#编程基础之枚举 2. c#编程基础之函数可变参数 3. c#编程基础之字符串基础 4. c#编程基础之字符串函数 5.c#编程基础之ref.ou ...
随机推荐
- 【KMP模板】简单写个KMP~
本来easy的KMP 却一直过不了洛谷的模板题... 仔细一看原来在输出next数组时打的回车而不是空格... 身败名裂... 话说有个sunday貌似一般状况下比KMP快呢...去看看2333 #i ...
- SAP Fiori应用的三种部署方式
封面图片来自Google搜索,关键字: Fiori Deployment 方式1 On premise环境下以BSP应用作为Fiori应用部署和运行的载体 在SAP成都labs我曾经担任过CRM这几个 ...
- (GO_GTD_1)基于OpenCV和QT,建立Android图像处理程序
一.创建新QT工程 一定要是全英文路径,比如"E:\android_qt_opencv\GO_GTD" 由于我们在安装的时候,选择android的工具链,所以在这里会出现以下选择, ...
- SQL 语句优化方法
尽量避免非操作符的使用,在索引上使用 NOT,<> 等操作符,数据库管理系统是不会使用索引的,可以将查询语句转化为可以使用索引的查询. 避免对查询的列的操作,任何对列的操作都可能导致全表扫 ...
- three.js 相机
图形学中的相机定义了三维空间到二维屏幕的投影方式,根据投影方式的不同,相机可分为 正交投影相机 与 透视投影相机. 正交投影相机 : 近处.远处的物体大小尺寸保持一致,常适用于工程制图.建模软件,如C ...
- javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)
前言 今天一个朋友让我帮他做一下tomcat的https配置,中间遇到了标题中这个错误,因此记录了一下过程,服务器.域名.证书.tomcat都已经准备好,就是需要配置一下即可,用的是阿里云的证书服务, ...
- 咫尺论坛|即速应用-微信小程序社区
咫尺论坛|即速应用-微信小程序社区 是一个集微信和支付宝小程序行业资讯.开发资源.技术交流于一身的大型小程序开发论坛,成立伊始便迅速聚集了一大批小程序开发爱好者,短时间内成为了国内领先的小程序开发者社 ...
- 重构仿vue社区代码
半年前根据vue社区提供的开放api制作的vue社区,功能大部分和原vue社区一样,还一些功能没做完,项目是半年前做的,已经过了半年,当时因为公司一个项目打算要vue来重构,提取小试牛刀做了个dom试 ...
- 一步一步创建ASP.NET MVC5程序[Repository+Autofac+Automapper+SqlSugar](三)
前言 上一篇<一步一步创建ASP.NET MVC5程序[Repository+Autofac+Automapper+SqlSugar](二)>我们通过如下操作: 创建实体及工具类 创建Re ...
- BZOJ 2038: [2009国家集训队]小Z的袜子(hose)【莫队算法裸题&&学习笔记】
2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec Memory Limit: 259 MBSubmit: 9894 Solved: 4561[Subm ...