zoukankan      html  css  js  c++  java
  • Socket网络编程--Libev库学习(1)

      这一节是安装篇。

      Socket网络编程不知不觉已经学了快两个月了。现在是时候找个网络库学学了。搜索了很多关于如何学网络编程的博客和问答。大致都是推荐学一个网络库,至于C++网络库有那么几个,各有各的好处。这里就选这个代码量少了,方便入门,等有一定的基础后,再看看“学之者生,用之着死”的ace或者有可能成为C++标准网络库的boost::asio,这个都是后话了。

      CentOS上安装软件最简单的当然是yum安装,我们可爱的libev好像现在还没有,那我们只能通过源码安装了。地址: http://dist.schmorp.de/libev/libev-4.15.tar.gz

    1 wget http://dist.schmorp.de/libev/libev-4.15.tar.gz
    2 tar -zxf libev-4.15.tar.gz
    3 cd libev-4.15
    4 ./configure
    5 make
    6 make install

      下面这些是安装信息

     1 Libraries have been installed in:
     2    /usr/local/lib
     3 
     4 If you ever happen to want to link against installed libraries
     5 in a given directory, LIBDIR, you must either use libtool, and
     6 specify the full pathname of the library, or use the `-LLIBDIR'
     7 flag during linking and do at least one of the following:
     8    - add LIBDIR to the `LD_LIBRARY_PATH' environment variable
     9      during execution
    10    - add LIBDIR to the `LD_RUN_PATH' environment variable
    11      during linking
    12    - use the `-Wl,-rpath -Wl,LIBDIR' linker flag
    13    - have your system administrator add LIBDIR to `/etc/ld.so.conf'
    14 
    15 See any operating system documentation about shared libraries for
    16 more information, such as the ld(1) and ld.so(8) manual pages.
    17 ----------------------------------------------------------------------
    18  /bin/mkdir -p '/usr/local/include'
    19  /usr/bin/install -c -m 644 ev.h ev++.h event.h '/usr/local/include'
    20  /bin/mkdir -p '/usr/local/share/man/man3'
    21  /usr/bin/install -c -m 644 ev.3 '/usr/local/share/man/man3'

       下面给出一个官方的例子

    #include <stdio.h>
    #include <ev.h> //ev库头文件
    
    //定义一个ev_TYPE 的结构体
    ev_io stdin_watcher;//定义一个stdin的观测者
    ev_timer timeout_watcher;
    
    
    //所有的watcher的回调函数都有相似的特点
    //当stdin有可读的数据时,将会调用下面这个回调函数
    static void stdin_cb(EV_P_ ev_io *w,int revents)
    {
        puts("stdin ready");
    
        //每一次时间都必须用对应的停止函数,手动的停止其watcher
        ev_io_stop(EV_A_ w);
        //这将导致所有嵌套执行的ev_run停止监听
        ev_break(EV_A_ EVBREAK_ALL);
    }
    
    //这是一个回调函数,用于定时器回调
    static void timeout_cb(EV_P_ ev_timer *w,int revents)
    {
        puts("timeout");
        //这将导致最早运行的ev_run停止监听
        ev_break(EV_A_ EVBREAK_ONE);
    }
    
    int main(int argc,char **args)
    {
        //使用一般默认的事件循环
        struct ev_loop *loop = EV_DEFAULT;
    
        //初始化一个I/O watcher,然后启动它
        ev_io_init(&stdin_watcher,stdin_cb,0,EV_READ);
        ev_io_start(loop,&stdin_watcher);
    
        //初始化一个定时器watcher,然后启动它,只有一次,没有重复的5.5秒定时
        ev_timer_init(&timeout_watcher,timeout_cb,5.5,0);
        ev_timer_start(loop,&timeout_watcher);
    
        //这里等待时间出发
        ev_run(loop,0);
    
        //撤销监听退出程序
        return 0;
    }

      编译 gcc server.c -lev -o server 就这样是可以编译通过的,但是执行就说找不到一个libev的库。看了一下上面的安装信息,才知道要设置一下环境变量。然后才可以运行。(/etc/profile或~/.bashrc)

    1 export LIBDIR=/usr/local/lib
    2 export LD_LIBRARY_PATH=/usr/local/lib
    3 export LD_RUN_PATH=/usr/local/lib

       用gcc -E选项编译后的源代码

     1 ev_io stdin_watcher;
     2 ev_timer timeout_watcher;
     3 
     4 
     5 
     6 
     7 static void stdin_cb(struct ev_loop *loop, ev_io *w,int revents)
     8 {
     9     puts("stdin ready");
    10 
    11 
    12     ev_io_stop(loop, w);
    13 
    14     ev_break(loop, EVBREAK_ALL);
    15 }
    16 
    17 
    18 static void timeout_cb(struct ev_loop *loop, ev_timer *w,int revents)
    19 {
    20     puts("timeout");
    21 
    22     ev_break(loop, EVBREAK_ONE);
    23 }
    24 
    25 int main(int argc,char **args)
    26 {
    27 
    28     struct ev_loop *loop = ev_default_loop (0);
    29 
    30 
    31     do { do { ((ev_watcher *)(void *)((&stdin_watcher)))->active = ((ev_watcher *)(void *)((&stdin_watcher)))->pending = 0; ( (ev_watcher *)(void *)(((&stdin_watcher))))->priority = (0); (((&stdin_watcher)))->cb = ((stdin_cb)); } while (0); do { ((&stdin_watcher))->fd = ((0)); ((&stdin_watcher))->events = ((EV_READ)) | EV__IOFDSET; } while (0); } while (0);
    32     ev_io_start(loop,&stdin_watcher);
    33 
    34 
    35     do { do { ((ev_watcher *)(void *)((&timeout_watcher)))->active = ((ev_watcher *)(void *)((&timeout_watcher)))->pending = 0; ( (ev_watcher *)(void *)(((&timeout_watcher))))->priority = (0); (((&timeout_watcher)))->cb = ((timeout_cb)); } while (0); do { ((ev_watcher_time *)((&timeout_watcher)))->at = ((5.5)); ((&timeout_watcher))->repeat = ((0)); } while (0); } while (0);
    36     ev_timer_start(loop,&timeout_watcher);
    37 
    38 
    39     ev_run(loop,0);
    40 
    41 
    42     return 0;
    43 }
     1     do {
     2         do {
     3             ((ev_watcher *)(void *)((&stdin_watcher)))->active = ((ev_watcher *)(void *)((&stdin_watcher)))->pending = 0;
     4             ( (ev_watcher *)(void *)(((&stdin_watcher))))->priority = (0);
     5             (((&stdin_watcher)))->cb = ((stdin_cb));
     6         } while (0);
     7         do {
     8             ((&stdin_watcher))->fd = ((0));
     9             ((&stdin_watcher))->events = ((EV_READ)) | EV__IOFDSET;
    10         } while (0);
    11     } while (0);
    12     ev_io_start(loop,&stdin_watcher);

       Libev通过一个struct ev_loop结构表示一个事件驱动的框架。在这个框架里面通过ev_xxx结构,ev_init、ev_xxx_set、ev_xxx_start接口箱这个事件驱动的框架里面注册事件监控器,当相应的事件监控器的事件出现时,便会触发该事件监控器的处理逻辑,去处理该事件。处理完之后,这些监控器进入到下一轮的监控中。符合一个标准的事件驱动状态的模型。

      Libev 除了提供了基本的三大类事件(IO事件、定时器事件、信号事件)外还提供了周期事件、子进程事件、文件状态改变事件等多个事件,这里我们用三大基本事件写一个例子。

     1 #include <stdio.h>
     2 #include <signal.h>
     3 #include <string.h>
     4 #include <sys/unistd.h>
     5 #include <ev.h>
     6 
     7 
     8 void io_action(struct ev_loop *main_loop,ev_io *io_w,int e)
     9 {
    10     int rst;
    11     char buf[1024];
    12     memset(buf,0,sizeof(buf));
    13     puts("In IO action");
    14     read(STDIN_FILENO,buf,sizeof(buf));
    15     buf[1023]='';
    16     printf("String: %s
    ",buf);
    17     ev_io_stop(main_loop,io_w);
    18 }
    19 
    20 void timer_action(struct ev_loop *main_loop,ev_timer *time_w,int e)
    21 {
    22     puts("In Time action");
    23     ev_timer_stop(main_loop,time_w);
    24 }
    25 
    26 void signal_action(struct ev_loop *main_loop,ev_signal *signal_w,int e)
    27 {
    28     puts("In Signal action");
    29     ev_signal_stop(main_loop,signal_w);
    30     ev_break(main_loop,EVBREAK_ALL);
    31 }
    32 
    33 int main(int argc,char **argv)
    34 {
    35     ev_io io_w;
    36     ev_timer timer_w;
    37     ev_signal signal_w;
    38     struct ev_loop *main_loop = ev_default_loop(0);
    39 
    40     ev_init(&io_w,io_action);
    41     ev_io_set(&io_w,STDIN_FILENO,EV_READ);
    42 
    43     ev_init(&timer_w,timer_action);
    44     ev_timer_set(&timer_w,2,0);
    45 
    46     ev_init(&signal_w,signal_action);
    47     ev_signal_set(&signal_w,SIGINT);
    48 
    49     ev_io_start(main_loop,&io_w);
    50     ev_timer_start(main_loop,&timer_w);
    51     ev_signal_start(main_loop,&signal_w);
    52 
    53     ev_run(main_loop,0);
    54     return 0;
    55 }

      该程序一直处于监听状态,直到有调用信号然后回调signal_w函数,该函数会调用ev_break函数退出ev_run的调用,如果注释掉第30行的代码,那么程序会在调用三个回调函数后才会结束(外包引用计数为0),否则一直监听着。具体ev_run和ev_break的参数说明如下:

    void ev_run (EV_P_ int flags);

    void ev_break (EV_P_ int how);

    同样我们这里比较关注flags和how这两个参数。flags有下面这几个:

    0:默认值。一直循环进行处理,直到外部引用计数==0或者是显示退出。
    EVRUN_NOWAIT:运行一次,poll时候不会等待。如果有pending事件进行处理,否则立即返回。
    EVRUN_ONCE:运行一次,poll时候会等待至少一个event发生,处理完成之后返回。
    而how有下面这几个:

    EVBREAK_ONE:只是退出一次ev_run这个调用。通常来说使用这个就可以了。
    EVBREAK_ALL:退出所有的ev_run调用。这种情况存在于ev_run在pengding处理时候会递归调用。

      第38行创建一个struct ev_loop *结构体,上面我们给出 ev_default_loop(0) 进行创建。使用libev的核心是事件循环,可以用 ev_default_loop 或 ev_loop_new 函数创建循环,或者直接使用 EV_DEFAULT 宏,区别是 ev_default_loop 创建的事件循环不是线程安全的,而 ev_loop_new 创建的事件循环不能捕捉信号和子进程的观察器。大多数情况下,可以像下面这样使用:

    if (!ev_default_loop (0))
        fatal ("could not initialise libev, bad $LIBEV_FLAGS in environment?");
    //或者明确选择一个后端:
    struct ev_loop *epoller = ev_loop_new (EVBACKEND_EPOLL | EVFLAG_NOENV);
    if (!epoller)
        fatal ("no epoll found here, maybe it hides under your chair");
    //如果需要动态分配循环的话,建议使用 ev_loop_new 和 ev_loop_destroy 。

      在创建子进程后,且想要使用事件循环时,需要先在子进程中调用 ev_default_fork 或 ev_loop_fork 来重新初始化后端的内核状态,它们分别对应 ev_default_loop 和 ev_loop_new 来使用。

      ev_run 启动事件循环。它的第二个参数为0时,将持续运行并处理循环直到没有活动的事件观察器或者调用了 ev_break 。另外两个取值是 EVRUN_NOWAIT 和 EVRUN_ONCE 。

      ev_break 跳出事件循环(在全部已发生的事件处理完之后)。第二个参数为 EVBREAK_ONE 或 EVBREAK_ALL 来指定跳出最内层的 ev_run 或者全部嵌套的 ev_run 。

      ev_suspend 和 ev_resume 用来暂停和重启事件循环,比如在程序挂起的时候。

      接下来创建观察器,它主要包括类型、触发条件和回调函数。将它注册到事件循环上,在满足注册的条件时,会触发观察器,调用它的回调函数。上面的例子中已经包含了IO观察器和计时观察器、信号观察器,此外还有周期观察器、文件状态观察器等等。初始化和设置观察器使用 ev_init 和 ev_TYPE_set ,也可以直接使用 ev_TYPE_init 。在特定事件循环上启动观察器使用 ev_TYPE_start 。 ev_TYPE_stop 停止观察器,并且会释放内存。libev中将观察器分为4种状态:初始化、启动/活动、等待、停止。libev中的观察器还支持优先级。

      下面将介绍各个观察器(watcher)

      ev_io  获取标准输入

    1 static void stdin_readable_cb (struct ev_loop *loop, ev_io *w, int revents)
    2 {
    3     ev_io_stop (loop, w);
    4     //.. read from stdin here (or from w->fd) and handle any I/O errors
    5 }
    6 
    7 ev_io stdin_readable;
    8 ev_io_init (&stdin_readable, stdin_readable_cb, STDIN_FILENO, EV_READ);
    9 ev_io_start (loop, &stdin_readable);

      ev_timer  创建一个 ?? 秒之后启动的计时器

    1 static void one_minute_cb (struct ev_loop *loop, ev_timer *w, int revents)
    2 {
    3     //创建一个60秒的计时器
    4     //.. one minute over, w is actually stopped right here
    5 }
    6 
    7 ev_timer mytimer;
    8 ev_timer_init (&mytimer, one_minute_cb, 60., 0.);
    9 ev_timer_start (loop, &mytimer);

      创建一个10s超时的超时器

     1 static void timeout_cb (struct ev_loop *loop, ev_timer *w, int revents)
     2 {
     3     //.. ten seconds without any activity
     4 }
     5 
     6 ev_timer mytimer;
     7 ev_timer_init (&mytimer, timeout_cb, 0., 10.); /* note, only repeat used */
     8 ev_timer_again (&mytimer); /* start timer */
     9 ev_run (loop, 0);
    10 
    11 // and in some piece of code that gets executed on any "activity":
    12 // reset the timeout to start ticking again at 10 seconds
    13 ev_timer_again (&mytimer);

      ev_periodic 创建一个小时为单位的周期定时器

    1 static void clock_cb (struct ev_loop *loop, ev_periodic *w, int revents)
    2 {
    3     // ... its now a full hour (UTC, or TAI or whatever your clock follows)
    4 }
    5 
    6 ev_periodic hourly_tick;
    7 ev_periodic_init (&hourly_tick, clock_cb, 0., 3600., 0);
    8 ev_periodic_start (loop, &hourly_tick);

      自定义周期计算方式

    1 #include <math.h>
    2 
    3 static ev_tstamp my_scheduler_cb (ev_periodic *w, ev_tstamp now)
    4 {
    5     return now + (3600. - fmod (now, 3600.));
    6 }
    7 
    8 ev_periodic_init (&hourly_tick, clock_cb, 0., 0., my_scheduler_cb);

      如果想从当前时间开始

    1 ev_periodic hourly_tick;
    2 ev_periodic_init (&hourly_tick, clock_cb, fmod (ev_now (loop), 3600.), 3600., 0);
    3 ev_periodic_start (loop, &hourly_tick);

      ev_signal 在收到 SIGINT 时做些清理

    1 static void sigint_cb (struct ev_loop *loop, ev_signal *w, int revents)
    2 {
    3     ev_break (loop, EVBREAK_ALL);
    4 }
    5 
    6 ev_signal signal_watcher;
    7 ev_signal_init (&signal_watcher, sigint_cb, SIGINT);
    8 ev_signal_start (loop, &signal_watcher);

      ev_child   fork 一个新进程,给它安装一个child处理器等待进程结束

     1 ev_child cw;
     2 
     3 static void
     4 child_cb (EV_P_ ev_child *w, int revents)
     5 {
     6     ev_child_stop (EV_A_ w);
     7     printf ("process %d exited with status %x
    ", w->rpid, w->rstatus);
     8 }
     9 
    10 pid_t pid = fork ();
    11 
    12 if (pid < 0)
    13     // error
    14 else if (pid == 0)
    15 {
    16      // the forked child executes here
    17      exit (1);
    18 }
    19 else
    20 {
    21     ev_child_init (&cw, child_cb, pid, 0);
    22     ev_child_start (EV_DEFAULT_ &cw);
    23 }

      ev_stat 文件状态观察器 

      监控/etc/passwd是否有变化

     1 static void passwd_cb (struct ev_loop *loop, ev_stat *w, int revents)
     2 {
     3     /* /etc/passwd changed in some way */
     4     if (w->attr.st_nlink)
     5     {
     6         printf ("passwd current size  %ld
    ", (long)w->attr.st_size);
     7         printf ("passwd current atime %ld
    ", (long)w->attr.st_mtime);
     8         printf ("passwd current mtime %ld
    ", (long)w->attr.st_mtime);
     9     }
    10     else
    11         /* you shalt not abuse printf for puts */
    12         puts ("wow, /etc/passwd is not there, expect problems. if this is windows, they already arrived
    ");
    13 }
    14 
    15 ...
    16 ev_stat passwd;
    17 
    18 ev_stat_init (&passwd, passwd_cb, "/etc/passwd", 0.);
    19 ev_stat_start (loop, &passwd);    

       下一小节就对那几个常用的事件驱动类型进行简单例子编写。

      参考资料: http://wenku.baidu.com/view/957ea001b52acfc789ebc9bf.html

          : Libev手册 http://cvs.schmorp.de/libev/ev.pod

          : http://my.oschina.net/u/917596/blog/176658

          : http://dirlt.com/libev.html

          : http://www.yeolar.com/note/2012/12/16/libev/

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

  • 相关阅读:
    Oracle创建database link(dblink)和同义词(synonym)
    spring boot 集成 quartz 定时任务
    tomcat 启动Spring boot 项目
    UUID+随机数
    js常用字符串处理方法
    Win10安装mysql-8.0.11-winx64详细步骤
    ORA-02049: 超时: 分布式事务处理等待锁
    spring boot 发邮件
    bootstrap table 列求和
    spring boot 延长 Session 时间
  • 原文地址:https://www.cnblogs.com/wunaozai/p/3950249.html
Copyright © 2011-2022 走看看