美文网首页
libevent示例

libevent示例

作者: Tomtoms | 来源:发表于2019-11-07 23:05 被阅读0次

    libevent示例

    基本概念

    Libevent是基于 Reactor 模式的网络库,在 Reactor 模式中,通常都有一个事件循环(Event Loop),在 Libevent 中,这个事件循环就是event_base结构体:

    struct event_base *event_base_new(void);         // 创建事件循环
    void event_base_free(struct event_base *base);   // 销毁事件循环
    int event_base_dispatch(struct event_base *base);// 运行事件循环
    

    通常来说,事件循环主要有两个作用:

    • 用来管理事件,比如说添加我们感兴趣的事件,修改事件或删除事件。
    • 用来轮询它管理的所有事件,如果发现有事件活跃 (avtive),就调用相应的回调函数去处理事件。
      Libevent 使用event结构体来代表事件,可以使用event_new()创建一个事件:
    struct event *event_new(struct event_base *base, // 事件循环
                     evutil_socket_t fd,      // 文件描述符
                     short what,              // 事件类型
                     event_callback_fn cb,    // 回调函数
                     void *arg);              // 传递给回调函数的参数
    

    创建一个事件之后,要怎么把它加入到事件循环呢?可以使用event_add()函数:

    int event_add(struct event *ev,             // 事件
                  const struct timeval *tv);    // 超时时间
    

    默认情况下,当一个事件变得活跃时,Libevent 会执行这个事件的回调函数,但同时也会将这个事件从事件循环中移除,例如,下面的程序,定时器只会触发一次:

    #include <event2/event.h>
    #include <iostream>
    #include <string>
    
    void timer_cb(evutil_socket_t fd, short what, void *arg)
    {
        auto str = static_cast<std::string *>(arg);
        std::cout << *str << std::endl;
    }
    
    int main()
    {
        std::string str = "Hello, World!";
        auto *base = event_base_new();
    
        struct timeval five_seconds = {1, 0};
        //auto *ev = event_new(base, -1, EV_TIMEOUT, timer_cb, (void *)&str);
        //EV_PERSIST可以控制时间不被移除
        auto *ev = event_new(base, -1, EV_TIMEOUT|EV_PERSIST, timer_cb, (void *)&str);
    
        event_add(ev, &five_seconds);
        event_base_dispatch(base);
    
        event_free(ev);
        event_base_free(base);
    
        return 0;
    }
    

    TCP Server

    Libevent 使用evconnlistener结构来表示 TCP Server,创建 TCP Server 的做法很简单:

    struct evconnlistener *evconnlistener_new_bind(
        struct event_base *base,        // 事件循环
        evconnlistener_cb cb, // 回调函数,当 accept() 成功时会被调用
        void *arg,                      // 传递给回调函数的参数
        unsigned flags,                 // 选项
        int backlog,                    // tcp backlog 参数
        const struct sockaddr *sa,      // 地址
        int socklen
    );
    void evconnlistener_free(struct evconnlistener *lev);
    

    调用evconnlistener_new_bind()函数之后,listening socket 会自动被设置成非阻塞的。我们还通过flags参数设置一些有用的选项,例如:

    • LEV_OPT_CLOSE_ON_FREE表示当调用evconnlistener_free()时,相应的 listening socket 也会被close()掉。
    • LEV_OPT_REUSEABLE表示会自动对 listening socket 设置SO_REUSEADDR这个 TCP 选项。
    #include <event2/listener.h>
    #include <arpa/inet.h>
    #include <string.h>
    #include <iostream>
    
    void accept_conn_cb(struct evconnlistener *listener, evutil_socket_t fd,
                        struct sockaddr *address, int socklen, void *arg)
    {
        char addr[INET_ADDRSTRLEN];
        auto *sin = reinterpret_cast<sockaddr_in *>(address);
        inet_ntop(AF_INET, &sin->sin_addr, addr, INET_ADDRSTRLEN);
    
        std::cout << "Accept TCP connection from: " << addr << std::endl;
    }
    
    void accept_error_cb(struct evconnlistener *listener, void *arg)
    {
        auto *base = evconnlistener_get_base(listener);
    
        // 跨平台的错误处理
        int err = EVUTIL_SOCKET_ERROR();
        std::cerr << "Got an error on the listener: "
                  << evutil_socket_error_to_string(err)
                  << std::endl;
        
        event_base_loopexit(base, NULL);
    }
    
    int main()
    {
        short port = 8000;
        struct sockaddr_in sin;
        memset(&sin, 0, sizeof(sin));
        sin.sin_family = AF_INET;
        sin.sin_addr.s_addr = htonl(INADDR_ANY);
        sin.sin_port = htons(port);
        
        auto *base = event_base_new();
        auto *listener = evconnlistener_new_bind(
            base, accept_conn_cb, nullptr,
            LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, -1,
            reinterpret_cast<struct sockaddr *>(&sin), sizeof(sin)
        );
    
        if (listener == nullptr) {
            std::cerr << "Couldn't create listener" << std::endl;
            return 1;
        }
    
        evconnlistener_set_error_cb(listener, accept_error_cb);
        event_base_dispatch(base);
        
        return 0;
    }
    
    

    相关文章

      网友评论

          本文标题:libevent示例

          本文链接:https://www.haomeiwen.com/subject/wumbbctx.html