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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM