libev的使用——结合Socket编程

之前自己学过一些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

http://wenku.baidu.com/link?url=z021OoHoevOJDNP1IVTmEcWvTKRoNKlfzi96k8gUKvcnvdZxlvW4diCihYlxFYRJVjtHSVtYrN451MIqlyznsmO0wUvjRslNPGOZopSIhP_

一.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的基本用法。

 

 
  1. // libev需要的头文件

  2. #include <ev.h>

  3. #include <stdio.h>

  4.  
  5. // 建立我们刚刚说的需要监听的事件,这些事件类型是libev提供的

  6. // with the name ev_TYPE

  7. //ev_io和ev_timer最为常用,ev_io为监听控制台输入,ev_timer为时间事件

  8. ev_io stdin_watcher;

  9. ev_timer timeout_watcher;

  10.  
  11. // 以下为自定义的回调函数,当触发监听事件时,调用执行对应的函数

  12.  
  13. // ev_io事件的回调函数,当有输入流stdin时,调用函数

  14. static void stdin_cb (EV_P_ ev_io *w, int revents)

  15. {

  16. puts ("stdin ready");

  17. //对ev_io事件的监控不会自动停止,需要手动在需要的时候停止

  18. ev_io_stop (EV_A_ w);

  19.  
  20. //整体的loop事件在所有监控停止时停止,也可以手动关闭全部的ev_run

  21. ev_break (EV_A_ EVBREAK_ALL);

  22. }

  23.  
  24. // 时间事件的自定义回调函数,可定时触发

  25. static void timeout_cb (EV_P_ ev_timer *w, intrevents)

  26. {

  27. puts ("timeout");

  28. //关闭最早的一个还在运行的ev_run

  29. ev_break (EV_A_ EVBREAK_ONE);

  30. }

  31.  
  32. int main (void)

  33. {

  34. //定义默认的 event loop,它就像一个大容器,可以装载着很多事件不停运行

  35. struct ev_loop *loop = EV_DEFAULT;

  36.  
  37. // 初始化ev_io事件监控,设置它的回调函数,和stdin

  38. ev_io_init (&stdin_watcher, stdin_cb,/*STDIN_FILENO*/ 0, EV_READ);

  39. //将ev_io事件放到event loop里面运行

  40. ev_io_start (loop, &stdin_watcher);

  41.  
  42. // 初始化ev_timer事件监控,设置它的回调函数,间隔时间,是否重复

  43. ev_timer_init (&timeout_watcher, timeout_cb, 5.5,0.);

  44. //将ev_timer事件放到event loop里面运行

  45. ev_timer_start (loop, &timeout_watcher);

  46.  
  47. // 将我们的大容器event loop整体运行起来

  48. ev_run (loop, 0);

  49.  
  50. // ev_run运行结束之后,才会运行到这里

  51. return 0;

  52. }

 

通过上面的例子,就可以知道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对应不同类型的时间监控,共有的标准化函数主要如下:

 

 
  1. typedef void (*)(struct ev_loop *loop, ev_TYPE*watcher, int revents) callback; // 回调函数都是这种类型

  2. ev_init (ev_TYPE *watcher, callback); // 初始化watcher

  3. ev_TYPE_set (ev_TYPE *watcher, [args]); // 设置watcher

  4. ev_TYPE_init (ev_TYPE *watcher, callback, [args]); // 这个函数综合了前两个函数功能

  5. ev_TYPE_start (loop, ev_TYPE *watcher); // 在ev_loop中注册watcher

  6. ev_TYPE_stop (loop, ev_TYPE *watcher); // 在ev_loop中注销watcher

  7. ev_set_priority (ev_TYPE *watcher, int priority); // 设置watcher优先级,值域为[-2,2],大而优先

  8. ev_feed_event (loop, ev_TYPE *watcher, int revents);// 这个做跨线程通知非常有用,相当于触发了某个事件。

  9. bool ev_is_active (ev_TYPE *watcher); // watcher是否active.

  10. bool ev_is_pending (ev_TYPE *watcher); // watcher是否pending.

  11. int ev_clear_pending (loop, ev_TYPE *watcher); // 清除watcher pending状态并且返回事件

 

3.ev_io相关

(1)ev_io结构

ev_io用来监听io事件,当有标准输入或输出时,则会触发事件,执行回调函数。

 

 
  1. typedef structev_io

  2. {

  3. EV_WATCHER_LIST (ev_io)

  4. int fd; /* 所监听io事件的文件描述符(file descriptor,非负数) */

  5. int events; /*所监听的事件,包括EV_READ, EV_WRITE 或 EV_READ | EV_WRITE */

  6. } 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是一个相对时间的定时器,会在给定的时间点触发事件,还可以在固定的时间间隔之后再次触发超时事件。

 

 
  1. typedef structev_timer

  2. {

  3. int active;

  4. int pending;

  5. int priority; /*优先级 */

  6. void *data; /* 参数*/

  7. void (*cb)(struct ev_loop *loop, struct ev_timer *w,int revents); /*回调函数 */

  8. ev_tstamp at; /* 定时器时间,单位为s*/

  9. ev_tstamp repeat; /*是否重复 */

  10. } 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可以方便地异步进行包的接收。下面的实例就实现的这个流程,直接用代码解释了:

 

 
  1. double time_span = 1.0; //发送消息的时间间隔

  2. int test_num = 5; //发送消息的次数

  3. int send_num = 0; //已发送消息的次数

  4. int rev_num = 0; //已收到回复的次数

  5. int fd = -1; //文件描述符

  6.  
  7. struct ev_loop *loop = EV_DEFAULT;

  8. ev_io io_watcher;

  9. ev_timer timer_watcher;

  10.  
  11. //ev_io的回调函数,用于异步接受回复

  12. void io_action(struct ev_loop *main_loop,ev_io*io_w,int e)

  13. {

  14. char buf[4096];

  15. char *ptr = (char *)buf;

  16. int ret = 0;

  17. //接收消息

  18. ret = ::recv(fd, ptr, 4096, 0);

  19.  
  20. std::cout << buf << "\n";

  21.  
  22. //接收到所有消息,停止ev_io事件

  23. if(rev_num >= test_num){

  24. ev_io_stop(main_loop,io_w);

  25. }

  26. }

  27.  
  28. //ev_timer的回调函数,用于定时发送消息

  29. void timer_action(struct ev_loop *main_loop,ev_timer*time_w,int e)

  30. {

  31. int ret;

  32. std::string msg_send = "send msg";

  33. //发送消息

  34. ret = ::send(fd, msg_send.c_str(),strlen(msg_send.c_str()), 0);

  35. std::cout << msg_send << "\n";

  36.  
  37. //发送足够数量的消息后,停止事件

  38. if(send_num >= test_num){

  39. ev_timer_stop(main_loop,time_w);

  40. }

  41. }

  42.  
  43. int main(int argc, char *argv[])

  44. {

  45. fd = socket(AF_INET, SOCK_STREAM, 0);

  46. ret = ::connect(fd, (struct sockaddr *)&addr,sizeof(addr));

  47.  
  48. ev_init(&io_watcher,io_action);

  49. ev_io_set(&io_watcher,fd,EV_READ);

  50.  
  51. ev_init(&timer_watcher,timer_action);

  52. ev_timer_set(&timer_watcher,time_span,1);

  53.  
  54. ev_io_start(loop,&io_watcher);

  55. ev_timer_start(loop,&timer_watcher);

  56.  
  57. ev_run(loop,0);

  58. return 0;

  59. }

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值