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”然后关闭连接。

  • 相关阅读:
    HashMap按键排序和按值排序
    LeetCode 91. Decode Ways
    LeetCode 459. Repeated Substring Pattern
    JVM
    LeetCode 385. Mini Parse
    LeetCode 319. Bulb Switcher
    LeetCode 343. Integer Break
    LeetCode 397. Integer Replacement
    LeetCode 3. Longest Substring Without Repeating Characters
    linux-网络数据包抓取-tcpdump
  • 原文地址:https://www.cnblogs.com/lit10050528/p/6171970.html
Copyright © 2011-2022 走看看