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. "段落"组件:<p> —— 快应用组件库H-UI

     <import name="p" src="../Common/ui/h-ui/text/c_p"></import> <te ...

  2. 2019-07-28【机器学习】无监督学习之聚类 DBSCAN方法及其应用 (在线大学生上网时间分析)

    样本: import numpy as np import sklearn.cluster as skc from sklearn import metrics import matplotlib.p ...

  3. 千亿级平台技术架构:为了支撑高并发,我把身份证存到了JS里

    @ 目录 一.用户信息安全规范 1.1 ​用户信息.敏感信息定义及判断依据 1.1.1 个人信息 1.1.2 个人敏感信息 1.2 ​用户信息存储的注意事项 二.​框架技术实现 2.1 用户敏感信息自 ...

  4. 用python代替人脑运算24点游戏

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

  5. Springboot:静态资源加载(七)

    WebMvc自动配置: 搜索WebMvcAutoConfiguration自动装配类: 第一种方式通过webjars加载静态资源: https://www.webjars.org(通过maven加载依 ...

  6. Java面试系列第2篇-Object类中的方法

    Java的Object是所有引用类型的父类,定义的方法按照用途可以分为以下几种: (1)构造函数 (2)hashCode() 和 equals() 函数用来判断对象是否相同 (3)wait().wai ...

  7. 【轮询】【ajax】【js】【spring boot】ajax超时请求:前端轮询处理超时请求解决方案 + spring boot服务设置接口超时时间的设置

    场景描述: ajax设置timeout在本机测试有效,但是在生产环境等外网环境无效的问题 1.ajax的timeout属性设置 前端请求超时事件[网络连接不稳定时候,就无效了] var data = ...

  8. 不停机还能替换代码?6年的 Java程序员表示不可思议

    相信很多人都有这样一种感受,自己写的代码在开发.测试环境跑的稳得一笔,可一到线上就抽风,不是缺这个就是少那个反正就是一顿报错,而线上调试代码又很麻烦,让人头疼得很.不过, 阿里巴巴出了一款名叫Arth ...

  9. 在项目中部署redis的读写分离架构(包含节点间认证口令)

    #### 在项目中部署redis的读写分离架构(包含节点间认证口令) ##### 1.配置过程 ---  1.此前就是已经将redis在系统中已经安装好了,redis utils目录下,有个redis ...

  10. Python中实现按顺序遍历字典

    第一种方法: import collections d = collections.OrderedDict([('a',1),('b',2),('c',3)]) ''' 或者把上面的那一行改成: d ...