libevent(六)http server中,作为一个单线程http server,不仅要监听每个连接的到来,还要监听每个连接上的I/O事件。

查看源码可知,在evhttp_bind_socket中设置了accept的回调函数:accept_socket_cb。

  1. /* Listener callback when a connection arrives at a server. */
  2. static void
  3. accept_socket_cb(struct evconnlistener *listener, evutil_socket_t nfd, struct sockaddr *peer_sa, int peer_socklen, void *arg)
  4. {
  5. struct evhttp *http = arg;
  6.  
  7. evhttp_get_request(http, nfd, peer_sa, peer_socklen);
  8. }
  1. static void
  2. evhttp_get_request(struct evhttp *http, evutil_socket_t fd,
  3. struct sockaddr *sa, ev_socklen_t salen)
  4. {
  5. struct evhttp_connection *evcon;
  6.  
  7. evcon = evhttp_get_request_connection(http, fd, sa, salen);
  8. if (evcon == NULL) {
  9. event_sock_warn(fd, "%s: cannot get connection on "EV_SOCK_FMT,
  10. __func__, EV_SOCK_ARG(fd));
  11. evutil_closesocket(fd);
  12. return;
  13. }
  14.  
  15. /* the timeout can be used by the server to close idle connections */
  16. if (http->timeout != -)
  17. evhttp_connection_set_timeout(evcon, http->timeout);
  18.  
  19. /*
  20. * if we want to accept more than one request on a connection,
  21. * we need to know which http server it belongs to.
  22. */
  23. evcon->http_server = http;
  24. TAILQ_INSERT_TAIL(&http->connections, evcon, next);
  25.  
  26. if (evhttp_associate_new_request_with_connection(evcon) == -)
  27. evhttp_connection_free(evcon);
  28. }

两个重要函数: evhttp_get_request_connectionevhttp_associate_new_request_with_connection

1. evhttp_get_request_connection

  1. /*
  2. * Takes a file descriptor to read a request from.
  3. * The callback is executed once the whole request has been read.
  4. */
  5.  
  6. static struct evhttp_connection*
  7. evhttp_get_request_connection(
  8. struct evhttp* http,
  9. evutil_socket_t fd, struct sockaddr *sa, ev_socklen_t salen)
  10. {
  11. struct evhttp_connection *evcon;
  12. char *hostname = NULL, *portname = NULL;
  13.  
  14. name_from_addr(sa, salen, &hostname, &portname);
  15. if (hostname == NULL || portname == NULL) {
  16. if (hostname) mm_free(hostname);
  17. if (portname) mm_free(portname);
  18. return (NULL);
  19. }
  20.  
  21. event_debug(("%s: new request from %s:%s on "EV_SOCK_FMT"\n",
  22. __func__, hostname, portname, EV_SOCK_ARG(fd)));
  23.  
  24. /* we need a connection object to put the http request on */
  25. evcon = evhttp_connection_base_new(
  26. http->base, NULL, hostname, atoi(portname));
  27. mm_free(hostname);
  28. mm_free(portname);
  29. if (evcon == NULL)
  30. return (NULL);
  31.  
  32. evcon->max_headers_size = http->default_max_headers_size;
  33. evcon->max_body_size = http->default_max_body_size;
  34.  
  35. evcon->flags |= EVHTTP_CON_INCOMING;
  36. evcon->state = EVCON_READING_FIRSTLINE;
  37.  
  38. evcon->fd = fd;
  39.  
  40. bufferevent_setfd(evcon->bufev, fd);
  41. return (evcon);
  42. }

注意点:

1. 通过evhttp_connection_base_new设置了bufferevent的readcd: evhttp_read_cb

  writecb: evhttp_write_cb。

2. 调用bufferevent_setfd

bufferevent_setfd代码如下:

  1. int
  2. bufferevent_setfd(struct bufferevent *bev, evutil_socket_t fd)
  3. {
  4. union bufferevent_ctrl_data d;
  5. int res = -;
  6. d.fd = fd;
  7. BEV_LOCK(bev);
  8. if (bev->be_ops->ctrl)
  9. res = bev->be_ops->ctrl(bev, BEV_CTRL_SET_FD, &d);
  10. BEV_UNLOCK(bev);
  11. return res;
  12. }
  13.  
  14. static int
  15. be_socket_ctrl(struct bufferevent *bev, enum bufferevent_ctrl_op op,
  16. union bufferevent_ctrl_data *data)
  17. {
  18. switch (op) {
  19. case BEV_CTRL_SET_FD:
  20. be_socket_setfd(bev, data->fd);
  21. return ;
  22. case BEV_CTRL_GET_FD:
  23. data->fd = event_get_fd(&bev->ev_read);
  24. return ;
  25. case BEV_CTRL_GET_UNDERLYING:
  26. case BEV_CTRL_CANCEL_ALL:
  27. default:
  28. return -;
  29. }
  30. }
  31.  
  32. static void
  33. be_socket_setfd(struct bufferevent *bufev, evutil_socket_t fd)
  34. {
  35. BEV_LOCK(bufev);
  36. EVUTIL_ASSERT(bufev->be_ops == &bufferevent_ops_socket);
  37.  
  38. event_del(&bufev->ev_read);
  39. event_del(&bufev->ev_write);
  40.  
  41. event_assign(&bufev->ev_read, bufev->ev_base, fd,
  42. EV_READ|EV_PERSIST, bufferevent_readcb, bufev);
  43. event_assign(&bufev->ev_write, bufev->ev_base, fd,
  44. EV_WRITE|EV_PERSIST, bufferevent_writecb, bufev);
  45.  
  46. if (fd >= )
  47. bufferevent_enable(bufev, bufev->enabled);
  48.  
  49. BEV_UNLOCK(bufev);
  50. }

这里主要设置fd的读事件回调bufferevent_readcb,写事件回调bufferevent_writecb。

(这里的bufferevent_enable可以不用在意,后面会重置。)

2. evhttp_associate_new_request_with_connection

  1. static int
  2. evhttp_associate_new_request_with_connection(struct evhttp_connection *evcon)
  3. {
  4. struct evhttp *http = evcon->http_server;
  5. struct evhttp_request *req;
  6. if ((req = evhttp_request_new(evhttp_handle_request, http)) == NULL)
  7. return (-);
  8.  
  9. if ((req->remote_host = mm_strdup(evcon->address)) == NULL) {
  10. event_warn("%s: strdup", __func__);
  11. evhttp_request_free(req);
  12. return (-);
  13. }
  14. req->remote_port = evcon->port;
  15.  
  16. req->evcon = evcon; /* the request ends up owning the connection */
  17. req->flags |= EVHTTP_REQ_OWN_CONNECTION;
  18.  
  19. /* We did not present the request to the user user yet, so treat it as
  20. * if the user was done with the request. This allows us to free the
  21. * request on a persistent connection if the client drops it without
  22. * sending a request.
  23. */
  24. req->userdone = ;
  25.  
  26. TAILQ_INSERT_TAIL(&evcon->requests, req, next);
  27.  
  28. req->kind = EVHTTP_REQUEST;
  29.  
  30. evhttp_start_read(evcon);
  31.  
  32. return ();
  33. }

第一步设置evhttp_request的回调函数evhttp_handle_request,第二步调用evhttp_start_read:

  1. /*
  2. * Reads data from file descriptor into request structure
  3. * Request structure needs to be set up correctly.
  4. */
  5.  
  6. void
  7. evhttp_start_read(struct evhttp_connection *evcon)
  8. {
  9. /* Set up an event to read the headers */
  10. bufferevent_disable(evcon->bufev, EV_WRITE);
  11. bufferevent_enable(evcon->bufev, EV_READ);
  12. evcon->state = EVCON_READING_FIRSTLINE;
  13. /* Reset the bufferevent callbacks */
  14. bufferevent_setcb(evcon->bufev,
  15. evhttp_read_cb,
  16. evhttp_write_cb,
  17. evhttp_error_cb,
  18. evcon);
  19.  
  20. /* If there's still data pending, process it next time through the
  21. * loop. Don't do it now; that could get recusive. */
  22. if (evbuffer_get_length(bufferevent_get_input(evcon->bufev))) {
  23. event_deferred_cb_schedule(get_deferred_queue(evcon),
  24. &evcon->read_more_deferred_cb);
  25. }
  26. }

可以看到,这里将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. SQL基础系列(2)-内置函数--转载w3school

    1.    日期函数 Mssql: SELECT GETDATE() 返回当前日期和时间 SELECT DATEPART(yyyy,OrderDate) AS OrderYear, DATEPART( ...

  2. canvas 实现光线沿不规则路径运动

    canvas 实现光线沿不规则路径运动 此文章为原创,请勿转载 1.svg实现 2.canvas实现 3.坑点 svg让动画沿着不规则路径运动 查阅svg文档后发现,svg动画运动有两种实现方式,且都 ...

  3. 通过Powershell检查SMTP地址是否被检测网站列入黑名单

    通常,我们可能因为某些用户发送了一些非常规邮件,邮件出口地址被一些权威网站列入黑名单,导致大量业务邮件无法正常发送.这时候,我们可以通过powershell写一些关于IP地址检测的脚本,并列入计划任务 ...

  4. CentOS7安装MYCAT中间件

    MYCAT是一个被广泛使用的功能强大的开源的数据库中间件,当然他的理想不仅仅是做一个中间件.这篇文章主要记录MYCAT服务的搭建过程,下篇会继续更新MYCAT的使用配置. 本篇记录将使用CentOS7 ...

  5. Arthas-Java的线上问题定位工具

    Arthas(阿尔萨斯) 能为你做什么? Arthas 是Alibaba开源的Java诊断工具,深受开发者喜爱. 当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决: 这个类从哪个 jar ...

  6. Tomorrow - 地形生成(1)

    原理很简单,请不要喷. 效果展示  种子输入框  种子为12345的地形  种子为23456的地形 代码展示 globalvar map random_set_seed(real(get_string ...

  7. java的多线程是如何实现的?和操作系统有什么关系?

    本文是作者原创,版权归作者所有.若要转载,请注明出处.本文只贴我觉得比较重要的源码,其他不重要非关键的就不贴了 本文操作系统是centos7 1.查看 pthread_create 函数显示及其示例 ...

  8. Kafka 2.5.0发布——弃用对Scala2.11的支持

    近日Kafka发布了最新版本 2.5.0,增加了很多新功能: 下载地址:https://kafka.apache.org/downloads#2.5.0 对TLS 1.3的支持(默认为1.2) 引入用 ...

  9. redis和memcache列出所有key

    //redis $redis = new Redis(); $redis->connect("host", "port"); $redis->sel ...

  10. Charles抓包——弱网测试(客户端)

    基础知识 网络延迟:网络延时指一个数据包从用户的计算机发送到网站服务器,然后再立即从网站服务器返回用户计算机的来回时间.通常使用网络管理工具PING(Packet Internet Grope)来测量 ...