zoukankan      html  css  js  c++  java
  • 示例的libevent的程序

    著作权归作者所有。
    商业转载请联系作者获得授权,非商业转载请注明出处。
    作者:auxten
    链接:http://zhuanlan.zhihu.com/auxten/20315482
    来源:知乎
    
    /*
        这是一个示例性质的libevent的程序,监听在TCP的9995端口。
        当连接建立成功后,它将会给Client回应一个消息"Hello, World!
    "
        发送完毕后就将连接关闭。
    
        程序也处理了SIGINT (ctrl-c)信号,收到这个信号后优雅退出程序。
    
        这个程序也用到了一些libevent比较高级的API:“bufferevent”
        这套API将buffer的“水位线”也抽象成了event来处理,灵感应该是来自
        Windows平台的IOCP。
    */
    
    // 引入常用Linux系统头文件 
    #include <string.h>
    #include <errno.h>
    #include <stdio.h>
    #include <signal.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>
    
    // 引入libevent 2.x相关的头文件 
    #include <event2/bufferevent.h>
    #include <event2/buffer.h>
    #include <event2/listener.h>
    #include <event2/util.h>
    #include <event2/event.h>
    
    // 定义字符串常量,将会回应给Client用 
    static const char MESSAGE[] = "Hello, World!
    ";
    
    // server监听的端口 
    static const int PORT = 9995;
    
    // 定义几个event callback的prototype(原型) 
    static void listener_cb(struct evconnlistener * , evutil_socket_t,
        struct sockaddr * , int socklen, void * );
    static void conn_writecb(struct bufferevent * , void * );
    static void conn_eventcb(struct bufferevent * , short, void * );
    static void signal_cb(evutil_socket_t, short, void * );
    
    // 定义标准的main函数 
    int
    main(int argc, char ** argv)
    {
        // event_base是整个event循环必要的结构体 
        struct event_base * base;
        // libevent的高级API专为监听的FD使用 
        struct evconnlistener * listener;
        // 信号处理event指针 
        struct event * signal_event;
        // 保存监听地址和端口的结构体 
        struct sockaddr_in sin;
    
        // 分配并初始化event_base 
        base = event_base_new();
        if (!base) {
            // 如果发生任何错误,向stderr(标准错误输出)打一条日志,退出 
            // 在C语言里,很多返回指针的API都以返回null为出错的返回值 
            // if (!base) 等价于 if (base == null) 
            fprintf(stderr, "Could not initialize libevent!
    ");
            return 1;
        }
    
        // 初始化sockaddr_in结构体,监听在0.0.0.0:9995 
        memset(&sin, 0, sizeof(sin));
        sin.sin_family = AF_INET;
        sin.sin_port = htons(PORT);
    
        // bind在上面制定的IP和端口,同时初始化listen的事件循环和callback:listener_cb 
        // 并把listener的事件循环注册在event_base:base上 
        listener = evconnlistener_new_bind(base, listener_cb, (void * )base,
            LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1,
            (struct sockaddr*)&sin,
            sizeof(sin));
    
        if (!listener) {
            // 如果发生任何错误,向stderr(标准错误输出)打一条日志,退出 
            fprintf(stderr, "Could not create a listener!
    ");
            return 1;
        }
    
        // 初始化信号处理event 
        signal_event = evsignal_new(base, SIGINT, signal_cb, (void * )base);
    
        // 把这个callback放入base中 
        if (!signal_event || event_add(signal_event, NULL)<0) {
            fprintf(stderr, "Could not create/add a signal event!
    ");
            return 1;
        }
    
        // 程序将在下面这一行内启动event循环,只有在调用event_base_loopexit后 
        // 才会从下面这个函数返回,并向下执行各种清理函数,导致整个程序退出 
        event_base_dispatch(base);
    
        // 各种清理free 
        evconnlistener_free(listener);
        event_free(signal_event);
        event_base_free(base);
    
        printf("done
    ");
        return 0;
    }
    
    // 监听端口的event callback 
    static void
    listener_cb(struct evconnlistener * listener, evutil_socket_t fd,
        struct sockaddr * sa, int socklen, void * user_data)
    {
        struct event_base * base = user_data;
        struct bufferevent * bev;
    
        // 新建一个bufferevent,设定BEV_OPT_CLOSE_ON_FREE, 
        // 保证bufferevent被free的时候fd也会被关闭 
        bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
        if (!bev) {
            fprintf(stderr, "Error constructing bufferevent!");
            event_base_loopbreak(base);
            return;
        }
        // 设定写buffer的event和其它event 
        bufferevent_setcb(bev, NULL, conn_writecb, conn_eventcb, NULL);
        // 开启向fd中写的event 
        bufferevent_enable(bev, EV_WRITE);
        // 关闭从fd中读写入buffer的event 
        bufferevent_disable(bev, EV_READ);
        // 向buffer中写入"Hello, World!
    " 
        // 上面的操作保证在fd可写时,将buffer中的内容写出去 
        bufferevent_write(bev, MESSAGE, strlen(MESSAGE));
    }
    
    
    // 每次fd可写,数据非阻塞写入后,会雕也难怪conn_writecb 
    // 这个函数每次检查eventbuffer的剩余大小,如果为0 
    // 表示数据已经全部写完,将eventbuffer free掉 
    // 由于在上面设定了BEV_OPT_CLOSE_ON_FREE,所以fd也会被关闭 
    static void
    conn_writecb(struct bufferevent * bev, void * user_data)
    {
        struct evbuffer * output = bufferevent_get_output(bev);
        if (evbuffer_get_length(output) == 0) {
            printf("flushed answer
    ");
            bufferevent_free(bev);
        }
    }
    
    // 处理读、写event之外的event的callback 
    static void
    conn_eventcb(struct bufferevent * bev, short events, void * user_data)
    {
        if (events & BEV_EVENT_EOF) {
            // Client端关闭连接 
            printf("Connection closed.
    ");
        } else if (events & BEV_EVENT_ERROR) {
            // 连接出错 
            printf("Got an error on the connection: %s
    ",
                strerror(errno));
        }
        // 如果还有其它的event没有处理,那就关闭这个bufferevent 
        bufferevent_free(bev);
    }
    
    // 信号处理event,收到SIGINT (ctrl-c)信号后,延迟2s退出event循环 
    static void
    signal_cb(evutil_socket_t sig, short events, void * user_data)
    {
        struct event_base * base = user_data;
        struct timeval delay = { 2, 0 };
    
        printf("Caught an interrupt signal; exiting cleanly in two seconds.
    ");
    
        event_base_loopexit(base, &delay);
    }
  • 相关阅读:
    【转】Oracle学习系列
    昏昏昏昏昏昏,怎么变成这样了。:(
    SQLServer 2K 安装重复出现挂起问题解决办法
    ORM iBATIS 学习,没弄清楚。
    MagicLinux让我用我一个方便的方法引导你吧。
    SOA大赛初赛文档已经提交.心中大石掉下.
    继续一下目标。
    ORM已经理解了.
    Spot the Bug Episode 2 中BUG的修改
    MaglicLinux启动加入了BOO.INI搞定.这个方便了.
  • 原文地址:https://www.cnblogs.com/noobkey/p/4942119.html
Copyright © 2011-2022 走看看