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















最新文章

  1. safehandle 和析构函数
  2. GridView,Repeater增加自动序号列
  3. How to Remove Table Partitioning in SQL Server
  4. log4net 日志跟踪程序
  5. js禁止页面复制 禁用页面右键菜单的代码
  6. BZOJ 3211 弗洛拉前往国家 树阵+并检查集合
  7. 《Java并发编程实战》第二章 线程安全 札记
  8. ORA-02266: 表中的唯一/主键被启用的外键引用
  9. Mybatis的@Options注解
  10. C# 比较两文本相似度
  11. Nginx配置CI框架问题(Linux平台下Centos系统)
  12. Nginx 提示host not found in upstream 错误解决方法
  13. 装饰者模式在JDK和Mybatis中是怎么应用的? java io包
  14. Linux 查看CPU信息,机器型号,内存等信息
  15. PHP解决网站大流量与高并发
  16. windows下mongodb集群搭建
  17. oracle 创建表空间 与创建用户与分配用户权限
  18. Could not find a package configuration file provided by 'ecl_threads' ,.................couldn't find required component 'ecl_threads'
  19. Python学习-3.Python的模块加载
  20. 「小程序JAVA实战」小程序的分享和下载功能(69)

热门文章

  1. JAVA+HttpServletRequest文件上传
  2. ES6构造函数class 和 ES5构造函数语法
  3. Davor COCI 2018
  4. 基于my-DAQ的温室迷你温室设计
  5. Thinking in Java,Fourth Edition(Java 编程思想,第四版)学习笔记(五)之Controlling Execution
  6. 架构师修炼之设计模式 - 策略模式(Strategy) 【Python与C#实现】
  7. vue2.x学习笔记(二十三)
  8. API联调神器PostMan使用详解
  9. Springboot:员工管理之添加员工(十(7))
  10. 使用openmp进行共享内存编程