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 != -)
- 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_connection、evhttp_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事件处理流程的更多相关文章
- Cocoa Touch事件处理流程--响应者链
Cocoa Touch事件处理流程--响应者链 作者:wangzz 原文地址:http://blog.csdn.net/wzzvictory/article/details/9264335 转载请注明 ...
- View的事件处理流程
一直对view的事件处理流程迷迷糊糊,今天花了点时间写了个栗子把它弄明白了. 1.view的常用的事件分为:单击事件(onClick).长按事件(onLongClick).触摸事件(onTouch), ...
- “全栈2019”Java第二十三章:流程控制语句中决策语句switch上篇
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- Python+Qt学习随笔:PyQt图形界面应用的事件处理流程
图形界面的事件处理是界面操作的核心,经过编写测试程序验证,基本确认PyQt图形界面应用程序的事件处理流程如下: 1.操作系统或其他应用发送消息给应用主程序: 2.应用主程序调用notify将消息队列中 ...
- Android touch事件处理流程
前面我们看了key事件的处理流程,相信大家对此已经有了新的认识,这篇文章我打算带领大家来看看稍微复杂些的touch 事件的处理流程.说它复杂是因为key事件本身就key down,up,long pr ...
- Android按键事件处理流程 -- KeyEvent
刚接触Android开发的时候,对touch.key事件的处理总是一知半解,一会是Activity里的方法,一会是各种View 中的,自己始终不清楚到底哪个在先哪个在后,总之对整个处理流程没能很好的把 ...
- libevent(九)evhttp
用libevent构建一个http server非常方便,可参考libevent(六)http server. 主要涉及的一个结构体是evhttp: struct evhttp { /* Next v ...
- OMXCodec与OMX事件处理流程
学习了解Mutilmedia Framework有一段时间了,今天闲下来稍微整理整理.OMXCodec.cpp类属于libstagefright,在整个MM PF 相当OMX的适配层,供awesome ...
- Nginx事件管理之事件处理流程
1. 概述 事件处理要解决的两个问题: "惊群" 问题,即多个 worker 子进程监听相同端口时,在 accept 建立新连接时会有争抢,引发不必要的上下文切换, 增加系统开销. ...
随机推荐
- SQL基础系列(2)-内置函数--转载w3school
1. 日期函数 Mssql: SELECT GETDATE() 返回当前日期和时间 SELECT DATEPART(yyyy,OrderDate) AS OrderYear, DATEPART( ...
- canvas 实现光线沿不规则路径运动
canvas 实现光线沿不规则路径运动 此文章为原创,请勿转载 1.svg实现 2.canvas实现 3.坑点 svg让动画沿着不规则路径运动 查阅svg文档后发现,svg动画运动有两种实现方式,且都 ...
- 通过Powershell检查SMTP地址是否被检测网站列入黑名单
通常,我们可能因为某些用户发送了一些非常规邮件,邮件出口地址被一些权威网站列入黑名单,导致大量业务邮件无法正常发送.这时候,我们可以通过powershell写一些关于IP地址检测的脚本,并列入计划任务 ...
- CentOS7安装MYCAT中间件
MYCAT是一个被广泛使用的功能强大的开源的数据库中间件,当然他的理想不仅仅是做一个中间件.这篇文章主要记录MYCAT服务的搭建过程,下篇会继续更新MYCAT的使用配置. 本篇记录将使用CentOS7 ...
- Arthas-Java的线上问题定位工具
Arthas(阿尔萨斯) 能为你做什么? Arthas 是Alibaba开源的Java诊断工具,深受开发者喜爱. 当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决: 这个类从哪个 jar ...
- Tomorrow - 地形生成(1)
原理很简单,请不要喷. 效果展示 种子输入框 种子为12345的地形 种子为23456的地形 代码展示 globalvar map random_set_seed(real(get_string ...
- java的多线程是如何实现的?和操作系统有什么关系?
本文是作者原创,版权归作者所有.若要转载,请注明出处.本文只贴我觉得比较重要的源码,其他不重要非关键的就不贴了 本文操作系统是centos7 1.查看 pthread_create 函数显示及其示例 ...
- Kafka 2.5.0发布——弃用对Scala2.11的支持
近日Kafka发布了最新版本 2.5.0,增加了很多新功能: 下载地址:https://kafka.apache.org/downloads#2.5.0 对TLS 1.3的支持(默认为1.2) 引入用 ...
- redis和memcache列出所有key
//redis $redis = new Redis(); $redis->connect("host", "port"); $redis->sel ...
- Charles抓包——弱网测试(客户端)
基础知识 网络延迟:网络延时指一个数据包从用户的计算机发送到网站服务器,然后再立即从网站服务器返回用户计算机的来回时间.通常使用网络管理工具PING(Packet Internet Grope)来测量 ...