目录(?)
[-]
close_local_endpoint函数:
struct mg_server {
sock_t listening_sock;
union socket_address lsa; // Listening socket address
struct ll active_connections;
struct ll uri_handlers;
char *config_options[NUM_OPTIONS];
void *server_data;
void *ssl_ctx; // SSL context
sock_t ctl[2]; // Control socketpair. Used to wake up from select() call
};
sock_t listening_sock;
union socket_address lsa; // Listening socket address
struct ll active_connections;
struct ll uri_handlers;
char *config_options[NUM_OPTIONS];
void *server_data;
void *ssl_ctx; // SSL context
sock_t ctl[2]; // Control socketpair. Used to wake up from select() call
};
active_connections是一个双向循环链表:
struct ll { struct ll *prev, *next; };
mg_create_server函数:
LINKED_LIST_INIT(&server->active_connections);
#define LINKED_LIST_INIT(N) ((N)->next = (N)->prev = (N))
accept_new_connection函数
该函数又是一个高级的if else if else的写法,作者的意图操作是:
- if(执行一个语句并判断)
- else if(再执行一个语句并判断) {异常处理}
- else if(再执行一个语句并判断) {异常处理}
- else {最终执行一些操作}
这里的厉害之处就在于:
每次if或者else if执行完语句后,故意让预期表达式为false,同而可以执行下一个分支,这样正常情况下,上面的三个if或else if判断中的表达式都会执行到,同时保证最终进入到else中完成所有的处理。
下面来分析函数的具体作用:
- sock = accept(server->listening_sock, &sa.sa, &len) accept一个连接
- check_acl(server->config_options[ACCESS_CONTROL_LIST], ntohl(* (uint32_t *) &sa.sin.sin_addr) 检查连接不是acl ACCESS_CONTROL_LIST
- conn = (struct connection *) calloc(1, sizeof(*conn)) calloc一个struct connection大小的空间,将指针赋给conn
- set_close_on_exec(sock); and set_non_blocking_mode(sock); 给sock设置参数
- 然后赋值:conn->server、conn->client_sock、conn->mg_conn.remote_ip、conn->mg_conn.remote_port以及conn->mg_conn.server_param
- LINKED_LIST_ADD_TO_FRONT(&server->active_connections, &conn->link); 将conn->link添加到server->active_connections循环链表中
注意:这里只是将conn->link添加到server->active_connections循环链表中,并没有添加和conn的指针相关的元素,这里又是一个技巧。获取conn的地方使用的另外一个宏是LINKED_LIST_ENTRY:
#define LINKED_LIST_ENTRY(P,T,N) ((T *)((char *)(P) - offsetof(T, N)))
调用时,传递的三个参数是:struct ll *变量、struct connection结构体以及link成员变量,一个使用的例子如下:
conn = LINKED_LIST_ENTRY(lp, struct connection, link);
LINKED_LIST_ENTRY宏定义分解来看:
- (char *)(P) 将P指针强转为char *
- offsetof(T, N) 计算结构体T中成员N在该结构体中的偏移量
- ((char *)(P) - offsetof(T, N)) 将P的指针往前减少相应的偏移量,即获得结构体T的指针
- ((T *)((char *)(P) - offsetof(T, N))) 最后强转为(T *)类型
mg_poll_server函数中
LINKED_LIST_FOREACH(&server->active_connections, lp, tmp) {
conn = LINKED_LIST_ENTRY(lp, struct connection, link);
conn = LINKED_LIST_ENTRY(lp, struct connection, link);
#define LINKED_LIST_FOREACH(H,N,T) \
for (N = (H)->next, T = (N)->next; N != (H); N = (T), T = (N)->next)
for (N = (H)->next, T = (N)->next; N != (H); N = (T), T = (N)->next)
该函数中有三处使用到这种遍历server->active_connections并取得struct connection *类型的指针的地方:
- add_to_set for socket select
- Read/write from clients
- Close expired connections and those that need to be closed
其中,close_conn(conn);时,会执行下述语句来移除server->active_connections列表中对应的节点:
LINKED_LIST_REMOVE(&conn->link);
从代码上看,只有在close_conn函数中才有调用到
LINKED_LIST_REMOVE(&conn->link)操作,但是close_conn函数只在mg_poll_server中有调用。
#define LINKED_LIST_REMOVE(N) do { ((N)->next)->prev = ((N)->prev); \
((N)->prev)->next = ((N)->next); LINKED_LIST_INIT(N); } while (0)
((N)->prev)->next = ((N)->next); LINKED_LIST_INIT(N); } while (0)
LINKED_LIST_REMOVE宏完成了将conn从server->active_connections列表中移除的功能。
然后closesocket(conn->client_sock)并依次free掉conn结构体中申请的空间以及最后free掉自身。
mg_destroy_server函数中,只是遍历所有的active_connections,然后free掉conn,但是并没有完成其它释放操作,如closesocket(conn->client_sock)、free(conn->request)等等,因此,此处需要改为也调用close_conn(conn)来完成所有的释放和销毁动作。
既然server->active_connections链表实际上是用来保存struct connection *数据的,那么要了解什么时候释放节点,则必须分析close_conn(conn)的调用处。
调用close_conn函数的地方有三处:
mg_poll_server函数:
else if (conn->flags & CONN_CLOSE) {
close_conn(conn);
}
close_conn(conn);
}
如果conn->flags标记中包含CONN_CLOSE,那么轮询处理中就会关闭该connection。
mg_poll_server函数:
if (conn->flags & CONN_CLOSE || conn->last_activity_time < expire_time) {
close_conn(conn);
}
close_conn(conn);
}
如果conn->flags标记中包含CONN_CLOSE或者连接的最后活动时间小于失效时间,则轮询处理中也会关闭该connection。
mg_destroy_server函数:
close_conn(conn);
此处本来就是要销毁server,调用close_conn来关闭和销毁资源。
write_to_client函数:
int n = conn->ssl == NULL ? send(conn->client_sock, io->buf, io->len, 0) : 0;
if (is_error(n)) {
conn->flags |= CONN_CLOSE;
}
conn->flags |= CONN_CLOSE;
}
如果发送数据失败,置conn->flags中CONN_CLOSE标记
write_to_client函数:
if (io->len == 0 && conn->flags & CONN_SPOOL_DONE) {
conn->flags |= CONN_CLOSE;
}
conn->flags |= CONN_CLOSE;
}
如果待发送数据为0(亦即发送数据完毕),并且CONN_SPOOL_DONE标记也为true,置conn->flags中CONN_CLOSE标记
read_from_client函数:
n = recv(conn->client_sock, buf, sizeof(buf), 0);
if (is_error(n)) {
conn->flags |= CONN_CLOSE;
}
conn->flags |= CONN_CLOSE;
}
如果接收数据失败,置conn->flags中CONN_CLOSE标记
close_local_endpoint函数:
if (keep_alive) {
process_request(conn); // Can call us recursively if pipelining is used
} else {
conn->flags |= conn->remote_iobuf.len == 0 ? CONN_CLOSE : CONN_SPOOL_DONE;
}
process_request(conn); // Can call us recursively if pipelining is used
} else {
conn->flags |= conn->remote_iobuf.len == 0 ? CONN_CLOSE : CONN_SPOOL_DONE;
}
如果不是keep_alive,并且数据已经发送完毕,置conn->flags中CONN_CLOSE标记
open_file_endpoint函数:
if (!strcmp(conn->mg_conn.request_method, "HEAD")) {
conn->flags |= CONN_SPOOL_DONE;
close(conn->endpoint.fd);
conn->endpoint_type = EP_NONE;
}
conn->flags |= CONN_SPOOL_DONE;
close(conn->endpoint.fd);
conn->endpoint_type = EP_NONE;
}
如果HTTP请求方法是HEAD(类似于GET, 但是不返回body信息,用于检查对象是否存在,以及得到对象的元数据),则置conn->flags中CONN_SPOOL_DONE标记
send_options函数:
conn->flags |= CONN_SPOOL_DONE;
如果HTTP请求方法是OPTIONS(询问可以执行哪些方法),则置conn->flags中CONN_SPOOL_DONE标记
close_local_endpoint函数:
if (keep_alive) {
process_request(conn); // Can call us recursively if pipelining is used
} else {
conn->flags |= conn->remote_iobuf.len == 0 ? CONN_CLOSE : CONN_SPOOL_DONE;
}
process_request(conn); // Can call us recursively if pipelining is used
} else {
conn->flags |= conn->remote_iobuf.len == 0 ? CONN_CLOSE : CONN_SPOOL_DONE;
}
如果不是keep_alive,并且数据尚未发送完毕,置conn->flags中CONN_SPOOL_DONE标记
综上分析,当HTTP HEAD或HTTP OPTIONS请求,或者需要进行半关闭(HTTP 300重定向或数据读取出错等等),如果这个时候数据没有传输完毕,需要置CONN_SPOOL_DONE标记
//TODO
//FIXME