逐步实现TCP服务端Step06-1:Libevent

Libevent是基于C语言开发的高性能开源通信库。Libevent是Reactor模式的一个实例。Reactor是一种事件处理模型,其基本形式: 其中,Reactor为核心部分,它是使用者启用Reactor机制的入口。EventDemultiplexer是事件分离器,Reactor使用它来监测事件的发生。Processor是事件处理器,它用于处理,或者说响应某个特定的事件。ConcreteProcessor的存在是为了方便模式使用者定制事件处理逻辑。Reactor、EventDemultiplexer和Processor三者基于句柄(Handle)建立关联,比如socket文件描述符。

在实际使用时,首先要用Reactor的RegisterProcessor方法注册事件处理器。注册事件处理器,就是告诉Reactor要监测的事件及这个事件的处理器。在RegisterProcessor中会调用EventDemultiplexer的RegisterEvent方法将该事件注册到事件分离器中,这样,当Demultiplex方法被调用时,就会监测该事件的就绪状态。

令整个Reactor机制工作起来的动力在于“事件循环”,其实就是一个无限循环,通过调用Reactor中的DoEventsLoop方法启动。在这个循环中会先用EventDemultiplexer的Demultiplex方法监测已注册的事件状态,再调用已就绪事件对应的Processor中的Process方法完成对事件的处理。

具体到Libevent,Reactor、EventDemultiplexer和Processor都有对应的实现。一个基于Libevent实现的echo服务程序(main.ccCMakeLists.txt):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include <cstring>
#include <iostream>

#include <event.h>
#include <event2/util.h>
#include <event2/event.h>
#include <event2/buffer.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>

void listener_cb(evconnlistener* listener, evutil_socket_t fd,
    sockaddr* sa, int socklen, void* para);
void conn_readcb(bufferevent* bev, void* para);
void conn_writecb(bufferevent* bev, void* para);
void conn_eventcb(bufferevent* bev, short events, void* para);

int main(int argc, char** argv)
{
    if (argc != 2) {
        std::cout << "Usage: " 
            << argv[0] 
            << " port" 
            << std::endl;
        return 0;
    }
    event_base* reactor = event_base_new();
    evconnlistener* listener;
    if (!reactor) {
        std::cout << "Event init failed." 
            << std::endl;
        return -1;
    }
    sockaddr_in sin;
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(atoi(argv[1]));
    listener = evconnlistener_new_bind(reactor, 
        listener_cb, (void*)reactor,
        LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE, 
        -1, (sockaddr*)&sin,
        sizeof(sin));
    if (!listener) {
        std::cout << "Could not create a listener!" 
            << std::endl;
        return 1;
    }
    event_base_dispatch(reactor);
    return 0;
}

void listener_cb(evconnlistener* listener, evutil_socket_t fd,
    sockaddr* sa, int socklen, void* para)
{
    event_base* reactor = (event_base*)para;
    bufferevent* bev = bufferevent_socket_new(reactor, 
        fd, BEV_OPT_CLOSE_ON_FREE);
    if (!bev) {
        std::cout << "Error constructing bufferevent!" 
            << std::endl;
        event_base_loopbreak(reactor);
        return;
    }
    bufferevent_setcb(bev, conn_readcb, 
        conn_writecb, conn_eventcb, NULL);
    bufferevent_enable(bev, EV_WRITE);
    bufferevent_enable(bev, EV_READ);
}

void conn_readcb(bufferevent* bev, void* para)
{
    evbuffer* src = bufferevent_get_input(bev);
    char buf[1024] = {0};
    int len = evbuffer_get_length(src);
    int n = evbuffer_remove(src, buf, sizeof(buf));
    if (n > 0)
        std::cout << buf;
    bufferevent_write(bev, buf, strlen(buf));
}

void conn_writecb(bufferevent* bev, void* para)
{
}

void conn_eventcb(bufferevent* bev, short events, void* para)
{
    if (events & BEV_EVENT_EOF) {
        std::cout << "Connection closed." 
            << std::endl;
    } else if (events & BEV_EVENT_ERROR) {
        std::cout << "Got an error on the connection: "
            << strerror(errno) << std::endl;
    }
    bufferevent_free(bev);
}

event_base_new返回一个event_base指针,它相当于Reactor,是后续操作的基础。由于Libevent基于C语言开发,方法和数据是分开的,在调用相应函数时,需将这个Reactor传入。

bufferevent_socket_new用于创建socket缓冲区类型的事件处理器。bufferevent指的是发生在用户缓冲区上的事件。当bufferevent事件处理的处理函数被触发的时候,就意味着Libevent已将数据从内核缓冲拷贝到用户缓冲,或已将用户缓冲中的数据拷贝到内核缓冲中。至于在内核缓冲中发生的事件,则由Libevent隐式处处理。通过bufferevent_setcb函数为bufferevent事件处理器指定具体的事件处理函数。

事件处理器已经创建完毕,下一步就是要向Reactor注册该处理器。注册函数为event_add,对于bufferevent来说,使用bufferevent_enable函数来完成注册操作。该函数最终也是通过event_add来完成注册的。

创建了Reactor和Processor,最后使用event_base_dispatch函数启动事件循环。该函数对应于Reactor中的DoEventsLoop方法。

由于在antframe中做了大量的封装工作,为简单起见,在使用Libevent重新实现netio时,还是基于其原始版本,即:imp_tcp_server工程中的那个netio 。具体的改动见imp_tcp_server/step06/01


<==  index  ==>