-
使用版本"7.14"
-
交互流程图如下:
-
关键函数
- mg_mgr_init函数
void mg_mgr_init(struct mg_mgr *mgr) {
memset(mgr, 0, sizeof(*mgr));
#if MG_ENABLE_EPOLL
if ((mgr->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0)
MG_ERROR(("epoll_create1 errno %d", errno));
#else
mgr->epoll_fd = -1;
#endif
#if MG_ARCH == MG_ARCH_WIN32 && MG_ENABLE_WINSOCK
// clang-format off
{ WSADATA data; WSAStartup(MAKEWORD(2, 2), &data); }
// clang-format on
#elif MG_ENABLE_FREERTOS_TCP
mgr->ss = FreeRTOS_CreateSocketSet();
#elif defined(__unix) || defined(__unix__) || defined(__APPLE__)
// Ignore SIGPIPE signal, so if client cancels the request, it
// won't kill the whole process.
signal(SIGPIPE, SIG_IGN);
#elif MG_ENABLE_TCPIP_DRIVER_INIT && defined(MG_TCPIP_DRIVER_INIT)
MG_TCPIP_DRIVER_INIT(mgr);
#endif
mgr->pipe = MG_INVALID_SOCKET;
mgr->dnstimeout = 3000;
mgr->dns4.url = "udp://8.8.8.8:53";
mgr->dns6.url = "udp://[2001:4860:4860::8888]:53";
mg_tls_ctx_init(mgr);
}
这是初始化一个mongoose管理结构体,在后面整个程序运行周期中都需要使用。结构体中的struct mg_connection *conns,是链接管理列表,每次有客户端连接都会创建一个id不一样的链接并添加到链接列表中。
- mg_http_listen函数
struct mg_connection *mg_http_listen(struct mg_mgr *mgr, const char *url,
mg_event_handler_t fn, void *fn_data) {
struct mg_connection *c = mg_listen(mgr, url, fn, fn_data);
if (c != NULL) c->pfn = http_cb;
return c;
}
struct mg_connection *mg_listen(struct mg_mgr *mgr, const char *url,
mg_event_handler_t fn, void *fn_data) {
struct mg_connection *c = NULL;
if ((c = mg_alloc_conn(mgr)) == NULL) {
MG_ERROR(("OOM %s", url));
} else if (!mg_open_listener(c, url)) {
MG_ERROR(("Failed: %s, errno %d", url, errno));
MG_PROF_FREE(c);
free(c);
c = NULL;
} else {
c->is_listening = 1;
c->is_udp = strncmp(url, "udp:", 4) == 0;
LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c);
c->fn = fn;
c->fn_data = fn_data;
mg_call(c, MG_EV_OPEN, NULL);
if (mg_url_is_ssl(url)) c->is_tls = 1; // Accepted connection must
MG_DEBUG(("%lu %ld %s", c->id, c->fd, url));
}
return c;
}
创建一个监听链接,专门用来监听有没有客户端连接。(这里仅仅只是创建一个链接并添加到链接列表中),c->is_listening = 1。并添加了http协议处理回调函数( c->pfn = http_cb)。这个是接收底层数据后,对底层数据进行解析的回调。并且注册了事件处理函数(c->fn = fn)。对http协议解析后进一步进行处理。
- mg_mgr_poll函数
struct mg_connection *c, *tmp;
uint64_t now;
printf("i----------------------------\r\n");
mg_iotest(mgr, ms);
now = mg_millis();
mg_timer_poll(&mgr->timers, now);
for (c = mgr->conns; c != NULL; c = tmp) {
printf("++++++++++++is_listening %d,is_udp %d\r\n",c->is_listening,c->is_udp);
bool is_resp = c->is_resp;
tmp = c->next;
mg_call(c, MG_EV_POLL, &now);
if (is_resp && !c->is_resp) {
printf("====================possible?\r\n");
long n = 0;
mg_call(c, MG_EV_READ, &n);
}
MG_VERBOSE(("%lu %c%c %c%c%c%c%c %lu %lu", c->id,
c->is_readable ? 'r' : '-', c->is_writable ? 'w' : '-',
c->is_tls ? 'T' : 't', c->is_connecting ? 'C' : 'c',
c->is_tls_hs ? 'H' : 'h', c->is_resolving ? 'R' : 'r',
c->is_closing ? 'C' : 'c', mg_tls_pending(c), c->rtls.len));
if (c->is_resolving || c->is_closing) {
// Do nothing
} else if (c->is_listening && c->is_udp == 0) {
if (c->is_readable) accept_conn(mgr, c);
} else if (c->is_connecting) {
if (c->is_readable || c->is_writable) connect_conn(c);
//} else if (c->is_tls_hs) {
// if ((c->is_readable || c->is_writable)) mg_tls_handshake(c);
} else {
if (c->is_readable) read_conn(c);
if (c->is_writable) write_conn(c);
}
if (c->is_draining && c->send.len == 0) c->is_closing = 1;
if (c->is_closing) close_conn(c);
}
}
- mg_iotest: 会去监听每个链接,查看链接是否有读的数据,写的数据,或者新的链接。c->is_readable/c->is_writable主要设置这两个变量。
- mg_timer_poll: 轮询处理定时器任务处理。
- mg_call(c, MG_EV_POLL, &now): 这个用的很妙,比如在发送比较大的文件时,会去调用如static_cb回调函数。每次轮询到此位置时,都会去进入到static_cb函数中,然后填充c->send缓冲区。
- mg_call(c, MG_EV_READ, &n): 这个是在数据处理完时,会去读有没有新的http请求进来。如果有继续处理?
- write_conn: 发送数据。
下面对个别函数进行分析
- mg_mkpipe
int mg_mkpipe(struct mg_mgr *mgr, mg_event_handler_t fn, void *fn_data,
bool udp) {
union usa usa[2];
MG_SOCKET_TYPE sp[2] = {MG_INVALID_SOCKET, MG_INVALID_SOCKET};
struct mg_connection *c = NULL;
if (!mg_socketpair(sp, usa, udp)) {
MG_ERROR(("Cannot create socket pair"));
} else if ((c = mg_wrapfd(mgr, (int) sp[1], fn, fn_data)) == NULL) {
closesocket(sp[0]);
closesocket(sp[1]);
sp[0] = sp[1] = MG_INVALID_SOCKET;
} else {
tomgaddr(&usa[0], &c->rem, false);
MG_DEBUG(("%lu %p pipe %lu", c->id, c->fd, (unsigned long) sp[0]));
}
return (int) sp[0];
}
- mg_socketpair 创建两个socket,并且connect上对方。 使用的ip为127.0.0.1,一般sp[1]为服务器端,sp[0] 为客户端。
- mg_wrapfd 将sp[1]弄进mg管理列表中
- 返回sp[0]客户端socket。