乐趣区

关于c:libevent学习使用1

根本应用办法

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-world
flushed 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 中写就能够了。这样一个简略的服务器就实现了。

退出移动版