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"\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代码如下:

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的输出缓冲。

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #93c86a }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #93c86a }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #93c86a }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #93c86a }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #93c86a }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #93c86a }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #93c86a }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #ffffff }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #93c86a }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #93c86a }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #93c86a }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #93c86a }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #93c86a }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #93c86a }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #93c86a }
span.s1 { }

时间: 2024-10-11 06:32:08

libevent(十三)evhttp事件处理流程的相关文章

Cocoa Touch事件处理流程--响应者链

Cocoa Touch事件处理流程--响应者链 一.事件分类 对于IOS设备用户来说,他们操作设备的方式主要有三种:触摸屏幕.晃动设备.通过遥控设施控制设备.对应的事件类型有以下三种: 1.触屏事件(Touch Event) 2.运动事件(Motion Event) 3.远端控制事件(Remote-Control Event) 今天以触屏事件(Touch Event)为例,来说明在Cocoa Touch框架中,事件的处理流程.首先不得不先介绍响应者链这个概念: 二.响应者链(Responder

事件代理与事件处理流程

1.事件代理(事件委托)定义:当我们要对多个元素添加事件时,可以将事件添加给它们的父节点,而将事件委托给父节点来触发函数.<ul id="parent-list"> <li id="post-1"><a href="#">我是第1</a></li> <li id="post-2"><a href="#">我是第2</a

Android touch事件处理流程 -- MotionEvent

前面我们看了key事件的处理流程,相信大家对此已经有了新的认识,这篇文章我打算带领大家来看看稍微复杂些的touch 事件的处理流程.说它复杂是因为key事件本身就key down,up,long pressed这几种,而touch事件支持多指触摸,给人的 感觉好像同时在发生多个touch事件一样,所以要处理的手指是多个而不是固定的一个,逻辑上当然也就复杂些了.不过本质 上还都是down.up.long pressed,touch的话还有move事件.接下来让我们直接进入本文的正题. 我们选择直接

Android按键事件处理流程 -- KeyEvent

刚接触Android开发的时候,对touch.key事件的处理总是一知半解,一会是Activity里的方法,一会是各种View 中的,自己始终不清楚到底哪个在先哪个在后,总之对整个处理流程没能很好的把握.每次写这部分代码的时候都有些心虚, 因为我不是很清楚什么时候.以什么样的顺序被调用,大都是打下log看看,没问题就算ok了.但随着时间流逝,这种感觉一直 折磨着我.期间也在网上搜索了相关资料,但总感觉不是那么令人满意.自打开始研究Android源码起,这部分内容的分析早就 被列在我的TODO l

View的事件处理流程

一直对view的事件处理流程迷迷糊糊,今天花了点时间写了个栗子把它弄明白了. 1.view的常用的事件分为:单击事件(onClick).长按事件(onLongClick).触摸事件(onTouch),另外view也有自己的onTouchEvent方法,这么多与事件相关的方法都由dispatchTouchEvent管理 2.根据面向对象思想,onTouch事件被封装成 MotionEvent 对象,常见的几个动作如图: 动作 简介 ACTION_DOWN 手指 初次接触到屏幕 时触发. ACTIO

MQTT---HiveMQ源码详解(十二)Netty-MQTT消息、事件处理(流程)

简介 前面这些章节,讲的基本上都是属于netty对MQTT周边的一些处理,由于MQTT协议总共目前可用的消息类型有14个,如果再加上对应的事件处理加载一起那就估计大概有14*3个handler,如果每个来讲一遍,难免有些枯燥,而且知识点会很分散,思考再三,想把整体的MQTT消息以及对应的事件处理作为一节来介绍,我们只讲它整体的实现思路.处理流程即可,这样对需要自己写broker的朋友的帮助应该是非常大的,这也符合最初写此系列博客的初衷. 热身 一.Callback 1.分类 HiveMQ的Cal

libevent(十)evhttp

用libevent构建一个http server非常方便,可参考libevent(七)http server. 主要涉及的一个结构体是 evhttp: struct evhttp { /* Next vhost, if this is a vhost. */ TAILQ_ENTRY(evhttp) next_vhost; /* All listeners for this host */ TAILQ_HEAD(boundq, evhttp_bound_socket) sockets; TAILQ

马哥教育第十三天系统启动流程、grub、模块、bash函数

kernel的功能:进程管理.文件系统.硬件驱动.内存管理.安全功能:SELinux.网络子系统,标准库:glibc 进程是运行在cpu上但用户进程受控于内核,用户进程需要完成特权指令时将触发软中断由用户模式转为内核模式,由内核代为执行特权指令并把结果返回给用户进程. Linux为单内核体系结构但是它支持模块化,模块还可以动态装载或卸载,Linux内核:核心 + 外围模块 核心:/boot/vmlinux-VERSION-release 模块:/lib/modules/VERSION-relea

OMXCodec与OMX事件处理流程

学习了解Mutilmedia Framework有一段时间了,今天闲下来稍微整理整理.OMXCodec.cpp类属于libstagefright,在整个MM PF 相当OMX的适配层,供awesomeplayer调用,而OMX.cpp,OMXNoteInstance.cpp等相当于OpenMax中的OpenMax IL,首先讲下OMXCodec与OMX callback事件的处理流程.先看整个流程的时序图吧: 从时序图看,首先我们要建立个OMXCodecObserver,该类是OMXCodec的