Socket网络编程--Libev库学习(2)

  这一小节讲各个观察器(Watcher)

  在libev下面watcher相当于EventHandler这么一个概念,通常里面会绑定fd回调函数以及我们需要关注的事件。 然后一旦触发事件之后会触发我们使用的回调函数,回调函数参数通常有reactor,watcher以及触发的事件。这里不打算重复文档里面的watcher 相关的内容和对应的API,但是对于某些内容的话可能会提到并且附带一些注释。之前我们还是看看通用过程,这里使用TYPE区分不同类型watcher.

 1 typedef void (*)(struct ev_loop *loop, ev_TYPE *watcher, int revents) callback; // 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); // 注册watcher
 6 ev_TYPE_stop (loop, ev_TYPE *watcher); // 注销watcher
 7 ev_set_priority (ev_TYPE *watcher, int priority); // 设置优先级
 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状态并且返回事件

  wacther的状态有下面这么几种:

  (1) initialiased.调用init函数初始化
  (2) active.调用start进行注册
  (3) pending.已经触发事件但是没有处理
  (4) inactive.调用stop注销。这个状态等同于initialised这个状态。

  ev_io

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <unistd.h>
 4 #include <ev.h>
 5
 6
 7 static void stdin_callback(struct ev_loop *loop,ev_io *w,int revents)
 8 {
 9     char str[1024];
10     if(revents & EV_READ)
11     {
12         //stdin might have data for us
13         printf("有数据可读\n");
14         scanf("%s",str);
15         ev_io_stop(loop,w);
16     }
17     else if(revents & EV_WRITE)
18     {
19         //stdout might have data for us
20         printf("有数据输出\n");
21         //ev_break(loop,EVBREAK_ONE);
22     }
23     printf("water:%d\n",ev_is_active(w));
24 }
25
26 int main(int argc,char **argv)
27 {
28     struct ev_loop * main_loop = ev_default_loop(0);
29     //这里的ev_default_loop可以使用ev_loop_new动态分配一个,然后使用ev_loop_destroy销毁。
30     //struct ev_loop * epoller = ev_loop_new(EVBACKEND_EPOLL | EVFLAG_NOENV);
31     //这里一般是使用EVBACKEND_EPOLL模型,同样的还有EVBACKEND_SELECT EVBACKEND_POLL EVBACKEND_KQUEUE EVBACKEND_DEVPOLL EVBACKEND_PORT 如果默认,那么ev会自动判断系统环境,选择最适合的模型,Linux一般为epoll bsd一般为kqueue什么的。
32     ev_io stdin_watcher;
33     ev_init(&stdin_watcher,stdin_callback);
34     ev_io_set(&stdin_watcher,STDIN_FILENO,EV_READ|EV_WRITE);
35     ev_io_start(main_loop,&stdin_watcher);
36
37     //ev_run(main_loop,EVRUN_ONCE);
38
39     //void ev_set_io_collect_interval (EV_P_ ev_tstamp interval);//这个是设置轮询的时间
40     //typedef double ev_tstamp
41     ev_set_io_collect_interval(main_loop,2.);//2秒
42     ev_run(main_loop,0);
43     //ev_is_active(ev_TYPE * watcher);//用于判断watcher是否为active
44     printf("main:%d\n",ev_is_active(&stdin_watcher));
45
46     //initialiased.调用init函数初始化
47     //active.调用start进行注册
48     //pending.已经触发事件但是没有处理
49     //inactive.调用stop注销。这个状态等同于initialised这个状态
50
51     return 0;
52 }

  ev_timer

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <unistd.h>
 4 #include <ev.h>
 5
 6 static void three_second_callback(struct ev_loop *loop,ev_timer *w,int revents)
 7 {
 8     //这是一个3秒触发的计时器
 9     printf("3秒触发器\n");
10 }
11 static void five_second_callback(struct ev_loop *loop,ev_timer *w,int revents)
12 {
13     //这是一个5秒触发的计时器
14     printf("5秒触发器\n");
15 }
16 static void the_second_callback(struct ev_loop *loop,ev_timer *w,int revents)
17 {
18     //这是一个10秒触发的计时器
19     printf("10秒触发器\n");
20 }
21
22 int main(int argc, char **args)
23 {
24     struct ev_loop * main_loop=ev_default_loop(0);
25
26     ev_timer mytimer_watcher3;
27     ev_timer mytimer_watcher5;
28
29     ev_init(&mytimer_watcher3,three_second_callback);
30     ev_timer_set(&mytimer_watcher3,3,0);
31     ev_timer_start(main_loop,&mytimer_watcher3);
32     ev_run(main_loop,0);//这个在ev_io上是一直判断的。但是这个触发器只会触发一次,不会每3秒触发一次。这是个问题。
33
34     ev_init(&mytimer_watcher5,five_second_callback);
35     ev_timer_set(&mytimer_watcher5,5,0);
36     ev_timer_start(main_loop,&mytimer_watcher5);
37     ev_run(main_loop,0);
38
39
40     ev_timer_start(main_loop,&mytimer_watcher3);
41     ev_timer_start(main_loop,&mytimer_watcher5);
42     ev_run(main_loop,0);//这里不会等待3,5秒,而是上一步后,直接输出,可见触发器只能用一次
43
44     ev_timer_set(&mytimer_watcher3,3,0);
45     ev_timer_start(main_loop,&mytimer_watcher3);
46     ev_timer_set(&mytimer_watcher5,5,0);
47     ev_timer_start(main_loop,&mytimer_watcher5);
48     ev_run(main_loop,0);//这里就会等待了,要重新set一遍
49
50
51     return 0;
52 }

  运行的结果是在第3秒输出(3秒触发器),第8秒输出(5秒触发器)(3秒触发器)(5秒触发器),第11秒输出(3秒触发器),第13秒输出(5秒触发器)。

  这个ev_timer居然不能重复,是不是没有解决办法呢?不是还有个ev_periodic这个可以实现周期性观察器。

  ev_periodic

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <unistd.h>
 4 #include <ev.h>
 5
 6 static void periodic_callback(struct ev_loop *loop,ev_periodic * w, int revents)
 7 {
 8     printf("每3秒执行一次\n");
 9     //ev_break(loop,EVBREAK_ALL);
10 }
11
12 //ev_tstamp=double
13 static ev_tstamp periodic_scheduler_callback(ev_periodic *w,ev_tstamp now)
14 {
15     return now+3;//注意时间要加上个now,是一个绝对时间
16 }
17
18 int main(int argc, char **args)
19 {
20     struct ev_loop * main_loop=ev_default_loop(0);
21
22     ev_periodic periodic_watcher;
23     //下面这个是第3个参数为3 是一个表达式
24     ev_init(&periodic_watcher,periodic_callback);
25     ev_periodic_set(&periodic_watcher,0,3,0);
26     ev_periodic_start(main_loop,&periodic_watcher);
27     ev_run(main_loop,0);
28
29     //如果时间周期计算方式,不能通过一个表达式来表示,那么可以通过一个函数来表示,放在set的第4个参数
30     ev_init(&periodic_watcher,periodic_callback);
31     ev_periodic_set(&periodic_watcher,0,0,periodic_scheduler_callback);
32     ev_periodic_start(main_loop,&periodic_watcher);
33     ev_run(main_loop,0);
34     //注意上下两部分不能通过运行,要注释掉一个才可以看到效果
35     return 0;
36 }

  ev_signal

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <unistd.h>
 4 #include <signal.h>
 5 #include <ev.h>
 6
 7 static void sigint_callback(struct ev_loop * loop,ev_signal *w,int revents)
 8 {
 9     if(revents & EV_SIGNAL)//用这个可以判断这次进来的是不是ev_signal 如果一个callback回调函数复用的话,就可以用这个来区分
10     {
11         printf("signal SIGINT\n");
12         ev_break(loop, EVBREAK_ALL);
13     }
14 }
15
16 static void sigquit_callback(struct ev_loop * loop,ev_signal *w,int revents)
17 {
18     printf("signal SIGQUIT\n");
19     ev_break(loop, EVBREAK_ALL);
20 }
21
22 int main(int argc, char **args)
23 {
24     struct ev_loop * main_loop=ev_default_loop(0);
25
26     ev_signal sigint_watcher;
27     ev_signal sigquit_watcher;
28
29     ev_init(&sigint_watcher,sigint_callback);
30     ev_signal_set(&sigint_watcher,SIGINT/*Other want to catch*/);//这里多个信号不能用或符号| 连接起来
31     ev_signal_start(main_loop,&sigint_watcher);
32
33     ev_init(&sigquit_watcher,sigquit_callback);
34     ev_signal_set(&sigquit_watcher,SIGQUIT/*Other want to catch*/);
35     ev_signal_start(main_loop,&sigquit_watcher);
36
37     ev_run(main_loop,0);
38
39     return 0;
40 }

  运行程序,输入Ctrl-C或Ctrl-\都是可以捕获的。

  ev_child

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <unistd.h>
 4 #include <stdlib.h>
 5 #include <sys/wait.h>
 6 #include <ev.h>
 7
 8 static void child_callback(struct ev_loop *loop,ev_child *w,int revents)
 9 {
10     ev_child_stop(loop,w);
11     printf("Process %d exited with status %d\n",w->rpid,w->rstatus);
12 }
13
14 int main(int argc, char **args)
15 {
16     struct ev_loop * main_loop=ev_default_loop(0);
17     pid_t pid;
18
19     ev_child child_watcher;
20
21     pid=fork();
22     if(pid<0)
23     {
24         printf("Fork Error\n");
25         return -1;
26     }
27     else if(pid==0)//child
28     {
29         printf("child doing..\n");
30         return 0;
31     }
32     else //father
33     {
34         sleep(2);//即使让子进程先执行,最后还是可以捕获到。
35         ev_init(&child_watcher,child_callback);
36         ev_child_set(&child_watcher,pid,0);
37         //ev_child_start(EV_DEFAULT_ &child_watcher);
38         ev_child_start(main_loop,&child_watcher);
39         ev_run(main_loop,0);
40     }
41
42     //waitpid(pid,0,0);
43     return 0;
44 }

  上面的例子,主进程通过pid将子进程绑定到了child_callback事件中,当子进程挂掉后,主进程就能捕捉的信号,然后调用child_callback函数。

  另一个测试场景:

  1 主进程启动后启动一个子进程。
  2 手动通过后台kill命令,kill掉子进程。
  3 主进程收到信息,打印出提示。

  上面代码第30行修改为while(1) ;  然后在第34行增加一行printf("pid:%d\n",pid); 然后运行,结果如下:

  注意上面有些是命令,有些是输出的中间结果。这样看起来很乱,但是终端运行结果就是这样。第1、6行是命令。

  ev_stat

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <unistd.h>
 4 #include <stdlib.h>
 5 #include <ev.h>
 6
 7 static void stat_callback(struct ev_loop *loop,ev_stat *w, int revents)
 8 {
 9     if(w->attr.st_nlink)
10     {
11         printf("The file size %ld\n",(long)w->attr.st_size);
12     }
13     else
14     {
15         printf("文件不存在\n");
16     }
17 }
18
19 int main(int argc, char **args)
20 {
21     struct ev_loop *main_loop=ev_default_loop(0);
22
23     ev_stat stat_watcher;
24     ev_init(&stat_watcher,stat_callback);
25     ev_stat_set(&stat_watcher,"/home/myuser/hello.txt",0);
26     ev_stat_start(main_loop,&stat_watcher);
27
28     ev_run(main_loop,0);
29     return 0;
30 }

  我们创建hello.txt这个文件,然后输入字符,然后保存,然后再打开,修改就这样。运行过程图

  文件attr的其他属性。

文档原文:The previous attributes of the file. The callback gets invoked whenever
C<prev> != C<attr>, or, more precisely, one or more of these members
differ: C<st_dev>, C<st_ino>, C<st_mode>, C<st_nlink>, C<st_uid>,
C<st_gid>, C<st_rdev>, C<st_size>, C<st_atime>, C<st_mtime>, C<st_ctime>文档解释:如果以前的文件有一点修改,无论是什么属性,都将触发这个回调函数。这个attr文件在这里可以获取到的属性成员有

  我们的stat_callback函数修改如下:

 1 static void stat_callback(struct ev_loop *loop,ev_stat *w, int revents)
 2 {
 3     if(w->attr.st_nlink)
 4     {
 5         printf("The file st_dev %d\n",w->attr.st_dev);
 6         printf("The file st_ino %d\n",w->attr.st_ino);
 7         printf("The file st_mode %d\n",w->attr.st_mode);
 8         printf("The file st_nlink %d\n",w->attr.st_nlink);
 9         printf("The file st_uid %d\n",w->attr.st_uid);
10         printf("The file st_gid %d\n",w->attr.st_gid);
11         printf("The file st_rdev %d\n",w->attr.st_rdev);
12         printf("The file st_size %d\n",w->attr.st_size);
13         printf("The file st_atime %d\n",w->attr.st_atime);
14         printf("The file st_mtime %d\n",w->attr.st_mtime);
15         printf("The file st_ctime %d\n",w->attr.st_ctime);
16     }
17     else
18     {
19         printf("文件不存在\n");
20     }
21 }

  运行结果:

  至于那些st_*的属性就不用说了,跟系统函数stat调用的返回结果是一样的。都是通用的。

  这一节到这里就结束了,这一节了解了几个最主要的watcher了。除了上面的几个外,还有下面这几个 ev_idle ev_prepare/ev_check ev_embed ev_fork ev_cleanup ev_async .

  本文地址: http://www.cnblogs.com/wunaozai/p/3954131.html

时间: 2024-07-31 17:04:49

Socket网络编程--Libev库学习(2)的相关文章

Socket网络编程--Libev库学习(3)

这一小节继续讲解各个观察器(Watcher). 上一小节已经讲解了ev_io(IO可读可写观察器),ev_stat(文件属性变化观察器),ev_signal(信号处理观察器),ev_timer(定时器),ev_periodic(周期任务处理),ev_child(子进程状态变化观察器).这一小节准备讲ev_fork(创建的进程时的观察器),ev_async(异步调用观察器),ev_cleanup(event loop退出时触发事件),ev_prepare(每次event loop之前事件),ev_

Linux程序设计学习笔记----Socket网络编程基础之TCP/IP协议簇

转载请注明出处: ,谢谢! 内容提要 本节主要学习网络通信基础,主要涉及的内容是: TCP/IP协议簇基础:两个模型 IPv4协议基础:IP地址分类与表示,子网掩码等 IP地址转换:点分十进制\二进制 TCP/IP协议簇基础 OSI模型 我们知道计算机网络之中,有各种各样的设备,那么如何实现这些设备的通信呢? 显然是通过标准的通讯协议,但是,整个网络连接的过程相当复杂,包括硬件.软件数据封包与应用程序的互相链接等等,如果想要写一支将联网全部功能都串连在一块的程序,那么当某个小环节出现问题时,整只

socket 网络编程快速入门(一)教你编写基于UDP/TCP的服务(客户端)通信

因为UNIX和Win的socket大同小异,为了方便和大众化,这里先介绍Winsock编程. socket 网络编程的难点在入门的时候就是对基本函数的了解和使用,因为这些函数的结构往往比较复杂,参数大部分都是结构体,令人难以记忆和理解. 但是一旦我们知道这些函数包括其参数的具体含义,socket网络编程也就变得不是那么复杂.这里不赘述 具体函数的详细含义,网络上有很多的文章,同时笔者建议大家参考 MSDN,对返回值,参数等会有更好的理解. 以下均为单线程的简单实例,多线程的请关注下一篇文章. (

windows下的socket网络编程(入门级)

windows下的socket网络编程 clinet.c 客户端 server.c 服务器端 UDP通信的实现 代码如下 已经很久没有在windows下编程了,这次因为需要做一个跨平台的网络程序,就先写了个简单的winSocket网路通信的例子,以便以后用到的时候有个参考. windows下使用winsock编程与linux/unix的区别在于windows下需要先有一个初始化的操作,结束的时候需要一个清理的操作.还有windows下编译的时候需要连接ws32_lib库. 大致过程如下 1.初始

Linux Socket 网络编程

Linux下的网络编程指的是socket套接字编程,入门比较简单.在学校里学过一些皮毛,平时就是自学玩,没有见识过真正的socket编程大程序,比较遗憾.总感觉每次看的时候都有收获,但是每次看完了之后,过段时间不看,重新拾起这些知识的时候又要从头开始,所以,在这里做个笔记也算是做个模板,以后可以直接从某一个阶段开始接着玩... 1. socket套接字介绍 socket机制其实就是包括socket, bind, listen, connect, accept等函数的方法,其通过指定的函数实现不同

Socket网络编程初探

MarkdownPad Document Socket网络编程初探 客户端/服务器架构 即C/S架构,其实web服务在某种意义上也算是C/S架构 一个特点是服务器端持续运行对外提供服务 为何学习socket一定要先学习互联网协议: C/S架构的软件是基于网络进行通信的 网络的核心就是一堆协议,即标准,想要开发一款基于网络通信的软件就必须遵循这些标准 socket是处在应用层和传输层中间的一组接口 说到这,那么socket到底是个什么呢?Socket是应用层与TCP/IP协议族通信的中间软件抽象层

Python全栈【Socket网络编程】

Python全栈[socket网络编程] 本章内容: Socket IO多路复用(select) SocketServer 模块(ThreadingTCPServer源码剖析) Socket socket通常也称作"套接字" 用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. socket起源于Unix,而Unix/Linux基本哲学之一就是"一切皆文件",对于文件用[打开][读写][关闭]模式

Python Socket 网络编程

Socket 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的,例如我们每天浏览网页.QQ 聊天.收发 email 等等.要解决网络上两台主机之间的进程通信问题,首先要唯一标识该进程,在 TCP/IP 网络协议中,就是通过 (IP地址,协议,端口号) 三元组来标识进程的,解决了进程标识问题,就有了通信的基础了. 本文主要介绍使用 Python 进行 TCP Socket 网络编程,假设你已

socket 网络编程高速入门(一)教你编写基于UDP/TCP的服务(client)通信

由于UNIX和Win的socket大同小异,为了方便和大众化,这里先介绍Winsock编程. socket 网络编程的难点在入门的时候就是对基本函数的了解和使用,由于这些函数的结构往往比較复杂,參数大部分都是结构体,令人难以记忆和理解. 可是一旦我们知道这些函数包含其參数的详细含义,socket网络编程也就变得不是那么复杂. 这里不赘述 详细函数的详细含义.网络上有非常多的文章.同一时候笔者建议大家參考 MSDN.对返回值,參数等会有更好的理解. 下面均为单线程的简单实例,多线程的请关注下一篇文