libevent示例

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 
#include 
#include 

void timer_cb(evutil_socket_t fd, short what, void *arg)
{
    auto str = static_cast(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 
#include 
#include 
#include 

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(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(&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示例)