• libevent(十三)evhttp事件处理流程


    libevent(六)http server中,作为一个单线程http server,不仅要监听每个连接的到来,还要监听每个连接上的I/O事件。

    查看源码可知,在evhttp_bind_socket中设置了accept的回调函数:accept_socket_cb。

    /* Listener callback when a connection arrives at a server. */
    static void
    accept_socket_cb(struct evconnlistener *listener, evutil_socket_t nfd, struct sockaddr *peer_sa, int peer_socklen, void *arg)
    {
        struct evhttp *http = arg;
    
        evhttp_get_request(http, nfd, peer_sa, peer_socklen);
    }
    static void
    evhttp_get_request(struct evhttp *http, evutil_socket_t fd,
        struct sockaddr *sa, ev_socklen_t salen)
    {
        struct evhttp_connection *evcon;
    
        evcon = evhttp_get_request_connection(http, fd, sa, salen);
        if (evcon == NULL) {
            event_sock_warn(fd, "%s: cannot get connection on "EV_SOCK_FMT,
                __func__, EV_SOCK_ARG(fd));
            evutil_closesocket(fd);
            return;
        }
    
        /* the timeout can be used by the server to close idle connections */
        if (http->timeout != -1)
            evhttp_connection_set_timeout(evcon, http->timeout);
    
        /*
         * if we want to accept more than one request on a connection,
         * we need to know which http server it belongs to.
         */
        evcon->http_server = http;
        TAILQ_INSERT_TAIL(&http->connections, evcon, next);
    
        if (evhttp_associate_new_request_with_connection(evcon) == -1)
            evhttp_connection_free(evcon);
    }

    两个重要函数: evhttp_get_request_connectionevhttp_associate_new_request_with_connection

    1. evhttp_get_request_connection 

    /*
     * Takes a file descriptor to read a request from.
     * The callback is executed once the whole request has been read.
     */
    
    static struct evhttp_connection*
    evhttp_get_request_connection(
        struct evhttp* http,
        evutil_socket_t fd, struct sockaddr *sa, ev_socklen_t salen)
    {
        struct evhttp_connection *evcon;
        char *hostname = NULL, *portname = NULL;
    
        name_from_addr(sa, salen, &hostname, &portname);
        if (hostname == NULL || portname == NULL) {
            if (hostname) mm_free(hostname);
            if (portname) mm_free(portname);
            return (NULL);
        }
    
        event_debug(("%s: new request from %s:%s on "EV_SOCK_FMT"
    ",
            __func__, hostname, portname, EV_SOCK_ARG(fd)));
    
        /* we need a connection object to put the http request on */
        evcon = evhttp_connection_base_new(
            http->base, NULL, hostname, atoi(portname));
        mm_free(hostname);
        mm_free(portname);
        if (evcon == NULL)
            return (NULL);
    
        evcon->max_headers_size = http->default_max_headers_size;
        evcon->max_body_size = http->default_max_body_size;
    
        evcon->flags |= EVHTTP_CON_INCOMING;
        evcon->state = EVCON_READING_FIRSTLINE;
    
        evcon->fd = fd;
    
        bufferevent_setfd(evcon->bufev, fd);
    
        return (evcon);
    }

    注意点:

    1. 通过evhttp_connection_base_new设置了bufferevent的readcd: evhttp_read_cb

      writecb: evhttp_write_cb。

    2. 调用bufferevent_setfd

    bufferevent_setfd代码如下:

    int
    bufferevent_setfd(struct bufferevent *bev, evutil_socket_t fd)
    {
        union bufferevent_ctrl_data d;
        int res = -1;
        d.fd = fd;
        BEV_LOCK(bev);
        if (bev->be_ops->ctrl)
            res = bev->be_ops->ctrl(bev, BEV_CTRL_SET_FD, &d);
        BEV_UNLOCK(bev);
        return res;
    }
    
    static int
    be_socket_ctrl(struct bufferevent *bev, enum bufferevent_ctrl_op op,
        union bufferevent_ctrl_data *data)
    {
        switch (op) {
        case BEV_CTRL_SET_FD:
            be_socket_setfd(bev, data->fd);
            return 0;
        case BEV_CTRL_GET_FD:
            data->fd = event_get_fd(&bev->ev_read);
            return 0;
        case BEV_CTRL_GET_UNDERLYING:
        case BEV_CTRL_CANCEL_ALL:
        default:
            return -1;
        }
    }
    
    static void
    be_socket_setfd(struct bufferevent *bufev, evutil_socket_t fd)
    {
        BEV_LOCK(bufev);
        EVUTIL_ASSERT(bufev->be_ops == &bufferevent_ops_socket);
    
        event_del(&bufev->ev_read);
        event_del(&bufev->ev_write);
    
        event_assign(&bufev->ev_read, bufev->ev_base, fd,
            EV_READ|EV_PERSIST, bufferevent_readcb, bufev);
        event_assign(&bufev->ev_write, bufev->ev_base, fd,
            EV_WRITE|EV_PERSIST, bufferevent_writecb, bufev);
    
        if (fd >= 0)
            bufferevent_enable(bufev, bufev->enabled);
    
        BEV_UNLOCK(bufev);
    }

    这里主要设置fd的读事件回调bufferevent_readcb,写事件回调bufferevent_writecb。

     (这里的bufferevent_enable可以不用在意,后面会重置。)

    2. evhttp_associate_new_request_with_connection

    static int
    evhttp_associate_new_request_with_connection(struct evhttp_connection *evcon)
    {
        struct evhttp *http = evcon->http_server;
        struct evhttp_request *req;
        if ((req = evhttp_request_new(evhttp_handle_request, http)) == NULL)
            return (-1);
    
        if ((req->remote_host = mm_strdup(evcon->address)) == NULL) {
            event_warn("%s: strdup", __func__);
            evhttp_request_free(req);
            return (-1);
        }
        req->remote_port = evcon->port;
    
        req->evcon = evcon;    /* the request ends up owning the connection */
        req->flags |= EVHTTP_REQ_OWN_CONNECTION;
    
        /* We did not present the request to the user user yet, so treat it as
         * if the user was done with the request.  This allows us to free the
         * request on a persistent connection if the client drops it without
         * sending a request.
         */
        req->userdone = 1;
    
        TAILQ_INSERT_TAIL(&evcon->requests, req, next);
    
        req->kind = EVHTTP_REQUEST;
    
    
        evhttp_start_read(evcon);
    
        return (0);
    }

    第一步设置evhttp_request的回调函数evhttp_handle_request,第二步调用evhttp_start_read:

    /*
     * Reads data from file descriptor into request structure
     * Request structure needs to be set up correctly.
     */
    
    void
    evhttp_start_read(struct evhttp_connection *evcon)
    {
        /* Set up an event to read the headers */
        bufferevent_disable(evcon->bufev, EV_WRITE);
        bufferevent_enable(evcon->bufev, EV_READ);
        evcon->state = EVCON_READING_FIRSTLINE;
        /* Reset the bufferevent callbacks */
        bufferevent_setcb(evcon->bufev,
            evhttp_read_cb,
            evhttp_write_cb,
            evhttp_error_cb,
            evcon);
    
        /* If there's still data pending, process it next time through the
         * loop.  Don't do it now; that could get recusive. */
        if (evbuffer_get_length(bufferevent_get_input(evcon->bufev))) {
            event_deferred_cb_schedule(get_deferred_queue(evcon),
                &evcon->read_more_deferred_cb);
        }
    }

    可以看到,这里将fd的读事件添加到了事件循环中。

     

    最后梳理下读事件调用流程:

    1. fd上有读事件发生

    2. bufferevent_readcb

    3. evhttp_read_cb

    4. evhttp_connection_done

    5. evhttp_handle_request

    6. 调用用户定义的evhttp回调函数

    关于数据的流向

    当fd上有读事件发生时,首先将fd上的数据读到evhttp_connection的bufferevent中,然后将bufferevent中的数据读到evhttp_request的输入缓冲中。

    当我们使用evhttp_send_reply发送数据时,首先将数据写入evhttp_request的输出缓冲中,然后写入evhttp_connection的bufferevent中,最后写入到fd的输出缓冲。

  • 相关阅读:
    hdu4829 带权并查集(题目不错)
    hdu4829 带权并查集(题目不错)
    洛谷 P1076 寻宝(模拟 && 剪枝)
    洛谷 P1981 表达式求值(模拟)
    洛谷 P2239 螺旋矩阵(模拟 && 数学)
    洛谷 P2118 比例简化(枚举)
    洛谷 P3956 棋盘(记忆化搜索)
    洛谷 P5018 对称二叉树(搜索)
    洛谷 P5016 龙虎斗(模拟)
    洛谷 P1563 玩具谜题(模拟)
  • 原文地址:https://www.cnblogs.com/gattaca/p/6913339.html
Copyright © 2020-2023  润新知