libevent(十三)evhttp事件處理流程

來源:互聯網
上載者:User

標籤:url   ack   發送資料   proc   not   blog   sts   ann   sse   

在libevent(六)http server中,作為一個單線程http server,不僅要監聽每個串連的到來,還要監聽每個串連上的I/O事件。

查看源碼可知,在evhttp_bind_socket中設定了accept的回呼函數:accept_socket_cb。

/* Listener callback when a connection arrives at a server. */static voidaccept_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 voidevhttp_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"\n",        __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代碼如下:

intbufferevent_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 intbe_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 voidbe_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 intevhttp_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. */voidevhttp_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的輸出緩衝。

 

libevent(十三)evhttp事件處理流程

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.