根本应用办法

libevent是一个应用事件驱动模型的网络网络库,网络开发,能够通过应用这个库,非常简单、清晰的代码做出一个反对I/O复用的程序。工作中须要应用到此库,所以记录一下学习进度。

根本应用能够参考源码的sample/目录下的应用示例,依据示例名称,我首先看一下 hello-workd.c 这个程序:代码不少,然而单个函数拆分来看,还是分清晰的。

首先是main 函数中:

    //......    base = event_base_new();    //......    listener = evconnlistener_new_bind(base, listener_cb, (void *)base,            LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1,            (struct sockaddr*)&sin, sizeof(sin));    //......    /// 注册了一个信号事件,该事件处理函数 signal_cb   解决的Ctrl+C信号        signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base);        if (!signal_event || event_add(signal_event, NULL)<0) {            fprintf(stderr, "Could not create/add a signal event!\n");            return 1;        }    /// 开启工夫轮询    event_base_dispatch(base);    /// 进行程序应用资源    evconnlistener_free(listener);    event_free(signal_event);    event_base_free(base);

首先调用 event_base_new 创立了一个 event_base 构造体,也不晓得是干嘛的,然而并没有给他提供任何网络相干的参数,临时先不论它。
之后调用了 evconnlistener_new_bind 函数,给他传入了网络的sockaddr构造信息,联合文件名看来就是在这里开始创立套接字和监听了。进入头文件listener.h 中也能看到对于该函数的介绍:

/**   Allocate a new evconnlistener object to listen for incoming TCP connections   on a given address.   @param base The event base to associate the listener with. event 会和 event_base 关联   @param cb A callback to be invoked when a new connection arrives. If the      callback is NULL, the listener will be treated as disabled until the      callback is set. 链接来的socket的处理函数   @param ptr A user-supplied pointer to give to the callback. 参数指针   @param flags Any number of LEV_OPT_* flags      @param backlog Passed to the listen() call to determine the length of the      acceptable connection backlog.  Set to -1 for a reasonable default.   @param sa The address to listen for connections on.    @param socklen The length of the address. */

能够看出 listener_cb 是解决链接客户端的socketLEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE能够依据listen.h中找到解释,设置了socket地址的可重用和敞开时开释, sa 是地址信息。

而后在 listener_cb 中,应该是解决链接的socket的数据的函数了,

bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);        if (!bev) {            fprintf(stderr, "Error constructing bufferevent!");            event_base_loopbreak(base);            return;        }        ///设置bufferevent的读写事件处理函数        bufferevent_setcb(bev, NULL, conn_writecb, conn_eventcb, NULL);        /// 使能写事件        bufferevent_enable(bev, EV_WRITE);        /// 失能读事件        bufferevent_disable(bev, EV_READ);        ///向bufferevent写入数据    bufferevent_write(bev, MESSAGE, strlen(MESSAGE));

如下面代码,依据头文件中的形容这个bufferevent构造的bev变量是和套接字fd关联的,而后应用bufferevent_setcb 有绑定了两个回调函数,写函数和异样处理函数。并enable了写,diable读,并向bev中写入了"Hello, World!\n"
进入两个回调函数中看一下,第一个

    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\n");            bufferevent_free(bev);        }}

这里有些看不明确的是,在之前的 listener_cb 当中曾经调用了一次bufferevent_write 写了数据,这里的写回调是有什么用途呢,
函数外面通过获取event_bufferoutputbuffer并判断其长度,如果为0,就开释资源,依据后面建设连贯时的flag参数能够晓得这里开释了资源就相当于敞开链接了。那就是给客户端写了一个hello world就退出链接了。。。(起初想到,这里的conn_writecb是为了保障outputt的数据曾经写完了,再敞开链接的用处)。

    static void    conn_eventcb(struct bufferevent *bev, short events, void *user_data)    {        if (events & BEV_EVENT_EOF) {   /// 写到完结即socket收到FIN后返回0            printf("Connection closed.\n");        } else if (events & BEV_EVENT_ERROR) {  /// socket出错            printf("Got an error on the connection: %s\n",                strerror(errno));/*XXX win32*/        }        /* None of the other events can happen here, since we haven't enabled         * timeouts */        bufferevent_free(bev);    }

conn_eventcb应该就是异样解决和完结解决的函数,没什么看的了。

看了代码之后,依据之气那的剖析看一下理论执行是不是这样子了:

咱们先执行一下编译的可执行程序,执行后在新窗口执行telnet 127.0.01 9995 后能够看到输入如下:

Trying 127.0.0.1...Connected to 127.0.0.1.Escape character is '^]'.Hello, World!Connection closed by foreign host.

执行 hello-word的窗口显示如下:

root@DESKTOP-RMCD5EP:/mnt/d/code/libevent/build/bin# ./hello-worldflushed answer

能够看到hello-world 执行的流程是:hello-world程序监听9995端口,telnet 链接上之后,hello-world 程序间接显示了 “flushed answer” 并给客户端的链接套接字写入了 “Hello, World!"(不晓得先后顺序),之后把客户端链接套接字敞开了。导致telnet 程序退出了。

除了 socket 解决,前面还有一个信号事件处理,基本上也是一样的流程,然而信号处理中先是应用 evsignal_new 函数新建一个事件,并同时将事件处理的信号类型以及函数指针传入,失去一个 event 构造,之后须要调用 evnet_add ,这个函数将没有找到具体的阐明,不过看起来像是把新建的 event 退出到惟一的 event_base 中。须要深入分析。之后等到信号传入时, hello-world 程序就会主动调用自定义的信号处理函数了。

总结一下

应用libevent,首先初始化一个event_base构造体,而后创立event构造,创立tcp服务器应用evconnlistener_new_bind函数,他返回一个evnet构造,之后将解决客户端链接的解决函数指针传入evconnlistener_new_bind,这个函数帮咱们实现了从创立socketconnect的解决,咱们只须要将解决客户但链接的函数指针传入就能够了。之后调用event_base_dispatch 函数 libevnet就主动开始监听了。

再解决客户端链接的时候,libevent提供了bufferevent,咱们能够将一个客户端链接socket绑定bufferevent,这个bufferevent替咱们做了读写socket获取传输内容的操作。当接管时调用bufferevent_read函数,当发送时间接调用 bufferevent_write 函数往befferevnet中写就能够了。这样一个简略的服务器就实现了。