zoukankan      html  css  js  c++  java
  • libevent源码分析:hello-world例子

    hello-world是libevent自带的一个例子,这个例子的作用是启动后监听一个端口,对于所有通过这个端口连接上服务器的程序发送一段字符:hello-world,然后关闭连接。

      1 /*
      2 * gcc -g -o hello-world hello-world.c -levent_core
      3 */
      4 #include <sys/socket.h>
      5 #include <netinet/in.h>
      6 #include <arpa/inet.h>
      7 #include <string.h>
      8 #include <errno.h>
      9 #include <stdio.h>
     10 #include <signal.h>
     11 
     12 #include <event2/bufferevent.h>
     13 #include <event2/buffer.h>
     14 #include <event2/listener.h>
     15 #include <event2/util.h>
     16 #include <event2/event.h>
     17 
     18 static const char MESSAGE[] = "Hello, World!
    ";
     19 
     20 static const int PORT = 9995;
     21 
     22 static void listener_cb(struct evconnlistener *, evutil_socket_t, struct sockaddr *, int socklen, void *);
     23 static void conn_writecb(struct bufferevent *, void *);
     24 static void conn_eventcb(struct bufferevent *, short, void *);
     25 static void signal_cb(evutil_socket_t, short, void *);
     26 
     27 int main(void)
     28 {
     29     struct event_base *base;
     30     struct evconnlistener *listener;
     31     struct event *signal_event;
     32     
     33     struct sockaddr_in sin;
     34 
     35     base = event_base_new();
     36     if (!base)
     37     {
     38         fprintf(stderr, "Could not initialize libevent
    ");
     39         return 1;
     40     }
     41 
     42     memset(&sin, 0, sizeof(sin));
     43     sin.sin_family = AF_INET;
     44     sin.sin_port = htons(PORT);
     45 
     46     listener = evconnlistener_new_bind(base, listener_cb, (void *)base, 
     47             LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE, -1, (struct sockaddr *)&sin, sizeof(sin));
     48 
     49     if (!listener)
     50     {
     51         fprintf(stderr, "Could not create a listener!
    ");
     52         return 1;
     53     }
     54     printf("Listening on %d
    ", PORT);
     55 
     56     signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base);
     57 
     58     if (!signal_event || event_add(signal_event, NULL) < 0)
     59     {
     60         fprintf(stderr, "Could not create/add a signal event!
    ");
     61         return 1;
     62     }
     63 
     64     event_base_dispatch(base);
     65 
     66     evconnlistener_free(listener);
     67     event_free(signal_event);
     68     event_base_free(base);
     69 
     70     printf("Done!
    ");
     71     return 0;
     72 }
     73 
     74 static void listener_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *sa, int socklen, void *user_data)
     75 {
     76     struct event_base *base = user_data;
     77     struct bufferevent *bev;
     78     struct sockaddr_in *sa_in = (struct sockaddr_in*)sa;
     79 
     80     bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
     81     if (!bev)
     82     {
     83         fprintf(stderr, "Error construction bufferevent!");
     84         event_base_loopbreak(base);
     85         return;
     86     }
     87     printf("Recv a new connection, ip[%s], port[%d]
    ", inet_ntoa(sa_in->sin_addr), ntohs(sa_in->sin_port));
     88     bufferevent_setcb(bev, NULL, conn_writecb, conn_eventcb, NULL);
     89     bufferevent_enable(bev, EV_WRITE);
     90     bufferevent_disable(bev, EV_READ);
     91 
     92     bufferevent_write(bev, MESSAGE, strlen(MESSAGE));
     93 }
     94 
     95 
     96 static void conn_writecb(struct bufferevent *bev, void *user_data)
     97 {
     98     struct evbuffer *output = bufferevent_get_output(bev);
     99     if (evbuffer_get_length(output) == 0)
    100     {
    101         printf("flushed answer
    ");
    102         bufferevent_free(bev);
    103     }
    104 }
    105 
    106 static void conn_eventcb(struct bufferevent *bev, short events, void *user_data)
    107 {
    108     if (events & BEV_EVENT_EOF)
    109     {
    110         printf("Connection closed.
    ");
    111     }
    112     else if (events & BEV_EVENT_ERROR)
    113     {
    114         printf("Got an error on the connection: %s
    ", strerror(errno));
    115     }
    116 
    117     bufferevent_free(bev);
    118 }
    119 
    120 static void signal_cb(evutil_socket_t sig, short events, void *user_data)
    121 {
    122     struct event_base *base = user_data;
    123     struct timeval delay = { 2, 0 };
    124     printf("Caught an interrupt signal; exiting cleanly in two seconds
    ");
    125     event_base_loopexit(base, &delay);
    126 }
    View Code

    下面就通过分析这个例子来看一下libevent对于IO事件是如何处理的:

    1、调用event_base_new获得event_base对象。

    2、调用evconnlistener_new_bind,返回一个struct evconnlistener对象指针,evconnlistener_new_bind函数内部实现如下:

    1)调用evutil_socket_函数获取一个socket。

    2)调用evconnlistener_new函数获取一个struct evconnlistener对象指针,并返回。

    在evconnlistener_new函数内部,首先调用malloc函数分配一个struct evconnlistener_event对象,然后利用传入的形参fd调用listen函数,然后初始化base的各个参数,调用event_assign初始化成员listener。

    其实这些函数归根结底还是会调用最基本的libevent函数,只是这些函数对基本的函数做了一些封装提供更高级、更方便的使用方式。

    struct evconnlistener和struct evconnlistener_event的定义如下:

     1 struct evconnlistener {
    2 const struct evconnlistener_ops *ops; 3 void *lock; 4 evconnlistener_cb cb; 5 evconnlistener_errorcb errorcb; 6 void *user_data; 7 unsigned flags; 8 short refcnt; 9 int accept4_flags; 10 unsigned enabled : 1; 11 }; 12 13 struct evconnlistener_event { 14 struct evconnlistener base; 15 struct event listener; 16 };

     关于这里,我有些不明白的是listener是如何被调用event_add函数的?答案在这里

    3、调用evsignal_new函数获取一个信号事件对象,其实这个函数也是对event_new函数的封装。

    1 #define evsignal_new(b, x, cb, arg)                
    2     event_new((b), (x), EV_SIGNAL|EV_PERSIST, (cb), (arg))

     4、调用event_base_dispatch函数进入事件循环。

    5、调用evconnlistener_free释放struct evconnlistener对象。

    然后看一下回调函数,在创建evconnlistener对象时传入了一个回调函数:listener_cb,该函数会在监听对象有新连接到来时被调用,在它的内部,首先创建一个struct bufferevent对象,然后设置该对象的回调函数,然后就是调用bufferevent_write函数将要发送的数据写入到该对象对应的buffer中,就不用管了。

    由此可见使用libevent库来开发网络服务器是多么的方便,高效。

    至此一个基于libevent的简单服务器就完成了,只能对连接上的客户端发送“hello world”然后关闭连接。

  • 相关阅读:
    前台ajax传数组,后台java接收
    CSS揭秘—打字动效(四)
    通过四个问题了解HTTP协议基础
    Fiddler抓包工具怎么设置HTTPS抓包
    固定定位导致$(window).scrollTop();获取滚动后到顶部距离总是为0
    git bash 使用自带 curl 命令出现乱码解决方法
    移动端布局方案—vw+rem
    Windows安装Nginx需要注意的地方
    orientation属性(判断是否为横竖屏)
    js之瀑布流的实现
  • 原文地址:https://www.cnblogs.com/lit10050528/p/6171970.html
Copyright © 2011-2022 走看看