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 != -)
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) == -)
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 = -;
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 ;
case BEV_CTRL_GET_FD:
data->fd = event_get_fd(&bev->ev_read);
return ;
case BEV_CTRL_GET_UNDERLYING:
case BEV_CTRL_CANCEL_ALL:
default:
return -;
}
} 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 >= )
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 (-); if ((req->remote_host = mm_strdup(evcon->address)) == NULL) {
event_warn("%s: strdup", __func__);
evhttp_request_free(req);
return (-);
}
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 = ; TAILQ_INSERT_TAIL(&evcon->requests, req, next); req->kind = EVHTTP_REQUEST; evhttp_start_read(evcon); return ();
}

第一步设置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的输出缓冲。















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

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

    Cocoa Touch事件处理流程--响应者链 作者:wangzz 原文地址:http://blog.csdn.net/wzzvictory/article/details/9264335 转载请注明 ...

  2. View的事件处理流程

    一直对view的事件处理流程迷迷糊糊,今天花了点时间写了个栗子把它弄明白了. 1.view的常用的事件分为:单击事件(onClick).长按事件(onLongClick).触摸事件(onTouch), ...

  3. “全栈2019”Java第二十三章:流程控制语句中决策语句switch上篇

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  4. Python+Qt学习随笔:PyQt图形界面应用的事件处理流程

    图形界面的事件处理是界面操作的核心,经过编写测试程序验证,基本确认PyQt图形界面应用程序的事件处理流程如下: 1.操作系统或其他应用发送消息给应用主程序: 2.应用主程序调用notify将消息队列中 ...

  5. Android touch事件处理流程

    前面我们看了key事件的处理流程,相信大家对此已经有了新的认识,这篇文章我打算带领大家来看看稍微复杂些的touch 事件的处理流程.说它复杂是因为key事件本身就key down,up,long pr ...

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

    刚接触Android开发的时候,对touch.key事件的处理总是一知半解,一会是Activity里的方法,一会是各种View 中的,自己始终不清楚到底哪个在先哪个在后,总之对整个处理流程没能很好的把 ...

  7. libevent(九)evhttp

    用libevent构建一个http server非常方便,可参考libevent(六)http server. 主要涉及的一个结构体是evhttp: struct evhttp { /* Next v ...

  8. OMXCodec与OMX事件处理流程

    学习了解Mutilmedia Framework有一段时间了,今天闲下来稍微整理整理.OMXCodec.cpp类属于libstagefright,在整个MM PF 相当OMX的适配层,供awesome ...

  9. Nginx事件管理之事件处理流程

    1. 概述 事件处理要解决的两个问题: "惊群" 问题,即多个 worker 子进程监听相同端口时,在 accept 建立新连接时会有争抢,引发不必要的上下文切换, 增加系统开销. ...

随机推荐

  1. mysql--> find your databases' local position

    1. find file:  "my.ini" 2.Using ctrl+F  find string "datadir" then you can see y ...

  2. 虚拟机安装windows sever2008

    1.打开并进行新建虚拟机 2.默认选择“典型” 3.选择“安装程序盘映像文件”,并‘浏览’选择本地的文件 4. 5.后面的默认选择即可,安装路径可自己修改 6.这一步的磁盘大小可自己修改的,这里先预设 ...

  3. tf.nn.relu 激活函数

    tf.nn.relu(features, name = None) 计算校正线性:max(features, 0) 参数: features:一个Tensor.必须是下列类型之一:float32,fl ...

  4. 使用Network Emulator Toolkit工具模拟网络丢包测试(上)

    弱网络测试包括延时和丢包二种场景下应用的功能是否正常: 网络延时测试使用Fiddler工具控制上下行数据传输延时时间来模拟网络延时场景: 网络丢包测试使用Network Emulator Toolki ...

  5. 关于node中两个模块相互引用却不会死循环的问题

    关于node中两个模块相互引用却不会死循环的问题 node中是通过require来导入加载模块的,require有两个作用: 1.加载文件模块并执行里面的代码 2.拿到被加载文件模块导出的接口对象 现 ...

  6. AJ学IOS(53)多线程网络之NSOperation简介

    AJ分享,必须精品 一:简单介绍 1:NSOperation的作⽤使用步骤: 配合使用NSOperation和NSOperationQueue也能实现多线程编程. NSOperation和NSOper ...

  7. 5000+图片找到你喜欢的那个TA,Python爬虫+颜值打分

    前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: 罗罗攀 PS:如有需要Python学习资料的小伙伴可以加点击下方链接 ...

  8. R - Cow and Message CodeForces - 1307C

    思路对了,但是不会写. 等差数列长度不是1就是2,所以不是一个字母就是俩字母,一开始写的时候直接枚举两个字母,然后让次数相乘.这样是不对的,比如abaabb,字母ab的个数应该是3+2+2,因该是每一 ...

  9. CentOS 使用中问题记录

    ⚠️使用yum提示Error: rpmdb open failed的解决方案 清除原rpmdb文件,这一步可能不用操作,直接进行第2步 # rm -f /var/lib/rpm/__db.* 重建rp ...

  10. jeecg ant design vue 一些收藏

    1关于 进来清除上次记录 找到src/permission.js下的