最近工作中有遇到使用mongoose作为WEB服务器在嵌入式设备中的,饶有兴趣的拜读了一下源码(版本为6.9),特此分享,如有错误还请指正。
mongoose作为一种轻量级的WEB服务器,适合在嵌入式设备中使用。其源码只有两个文件:moogoose.c、mongoose.h。
对于网络编程学的差的我来说, 还算比较容易理解,以linux环境为例,从以下几个方面来介绍。
1. 数据结构
1.1 mg_mgr是mongoose中进行事件管理的结构体,事件分为5种类型, 共享同一个回调函数,事件类型通过传参区分。
#define MG_EV_POLL 0 /* Sent to each connection on each mg_mgr_poll() call */ #define MG_EV_ACCEPT 1 /* New connection accepted. union socket_address * */ #define MG_EV_CONNECT 2 /* connect() succeeded or failed. int * */ #define MG_EV_RECV 3 /* Data has been received. int *num_bytes */ #define MG_EV_SEND 4 /* Data has been written to a socket. int *num_bytes */ #define MG_EV_CLOSE 5 /* Connection is closed. NULL */ #define MG_EV_TIMER 6 /* now >= conn->ev_timer_time. double * */
重要成员:
active_connections :当前活动的连接,如果有多个,则以链表形式挂接
ctl: broadcast 的socket
ifaces:网络相关的接口集合,在linux下默认为socket相关接口
/* * Mongoose event manager. */ struct mg_mgr { struct mg_connection *active_connections; #if MG_ENABLE_HEXDUMP const char *hexdump_file; /* Debug hexdump file path */ #endif #if MG_ENABLE_BROADCAST sock_t ctl[2]; /* Socketpair for mg_broadcast() */ #endif void *user_data; /* User data */ int num_ifaces; struct mg_iface **ifaces; /* network interfaces */ const char *nameserver; /* DNS server to use */ };
#define MG_SOCKET_IFACE_VTABLE \
{ \
mg_socket_if_init, \
mg_socket_if_free, \
mg_socket_if_add_conn, \
mg_socket_if_remove_conn, \
mg_socket_if_poll, \
mg_socket_if_listen_tcp, \
mg_socket_if_listen_udp, \
mg_socket_if_connect_tcp, \
mg_socket_if_connect_udp, \
mg_socket_if_tcp_send, \
mg_socket_if_udp_send, \
mg_socket_if_recved, \
mg_socket_if_create_conn, \
mg_socket_if_destroy_conn, \
mg_socket_if_sock_set, \
mg_socket_if_get_conn_addr, \
}
1.2 mg_connection是一个具体连接实例
结构体重要成员:
1. next、prev: 下、上一个连接
2. mgr:对应的事件管理
3. sock: 对应的socket
4. sa: socket的地址
5.recv_mbuf、send_mbuf : 发送和接受的buffer
6. proto_handler、handler: 协议的回调函数和事件回调函数
/* * Mongoose connection. */ struct mg_connection { struct mg_connection *next, *prev; /* mg_mgr::active_connections linkage */ struct mg_connection *listener; /* Set only for accept()-ed connections */ struct mg_mgr *mgr; /* Pointer to containing manager */ sock_t sock; /* Socket to the remote peer */ int err; union socket_address sa; /* Remote peer address */ size_t recv_mbuf_limit; /* Max size of recv buffer */ struct mbuf recv_mbuf; /* Received data */ struct mbuf send_mbuf; /* Data scheduled for sending */ time_t last_io_time; /* Timestamp of the last socket IO */ double ev_timer_time; /* Timestamp of the future MG_EV_TIMER */ #if MG_ENABLE_SSL void *ssl_if_data; /* SSL library data. */ #endif mg_event_handler_t proto_handler; /* Protocol-specific event handler */ void *proto_data; /* Protocol-specific data */ void (*proto_data_destructor)(void *proto_data); mg_event_handler_t handler; /* Event handler function */ void *user_data; /* User-specific data */ union { void *v; /* * the C standard is fussy about fitting function pointers into * void pointers, since some archs might have fat pointers for functions. */ mg_event_handler_t f; } priv_1; void *priv_2; void *mgr_data; /* Implementation-specific event manager's data. */ struct mg_iface *iface; unsigned long flags; /* Flags set by Mongoose */ #define MG_F_LISTENING (1 << 0) /* This connection is listening */ #define MG_F_UDP (1 << 1) /* This connection is UDP */ #define MG_F_RESOLVING (1 << 2) /* Waiting for async resolver */ #define MG_F_CONNECTING (1 << 3) /* connect() call in progress */ #define MG_F_SSL (1 << 4) /* SSL is enabled on the connection */ #define MG_F_SSL_HANDSHAKE_DONE (1 << 5) /* SSL hanshake has completed */ #define MG_F_WANT_READ (1 << 6) /* SSL specific */ #define MG_F_WANT_WRITE (1 << 7) /* SSL specific */ #define MG_F_IS_WEBSOCKET (1 << 8) /* Websocket specific */ /* Flags that are settable by user */ #define MG_F_SEND_AND_CLOSE (1 << 10) /* Push remaining data and close */ #define MG_F_CLOSE_IMMEDIATELY (1 << 11) /* Disconnect */ #define MG_F_WEBSOCKET_NO_DEFRAG (1 << 12) /* Websocket specific */ #define MG_F_DELETE_CHUNK (1 << 13) /* HTTP specific */ #define MG_F_ENABLE_BROADCAST (1 << 14) /* Allow broadcast address usage */ #define MG_F_TUN_DO_NOT_RECONNECT (1 << 15) /* Don't reconnect tunnel */ #define MG_F_USER_1 (1 << 20) /* Flags left for application */ #define MG_F_USER_2 (1 << 21) #define MG_F_USER_3 (1 << 22) #define MG_F_USER_4 (1 << 23) #define MG_F_USER_5 (1 << 24) #define MG_F_USER_6 (1 << 25) };
2. 实现方式
整个流程其实很简单,可分为以下三步
1. mg_mgr_init 先对mgr进行初始化,主要是将相关的socket接口函数集合赋值给mgr.ifaces
2. mg_bind 该步骤主要为一个mg_connection申请内存,并将事件回调函数ev_handler注册到该连接里,并且初始化若干个(由网卡数量决定)
http端口的socket进行监听
3. mg_mgr_poll 该函数调用mongoose中提供的poll接口:mg_socket_if_poll。在该函数中,对所有初始化的socket进行select操作,
在退出select的阻塞后,根据read_fd_set, write_fd_set, err_fd_set 进行判断,将退出阻塞的socket分类,然后进行分类处理。
int main(void) { struct mg_mgr mgr; struct mg_connection *nc; cs_stat_t st; mg_mgr_init(&mgr, NULL); nc = mg_bind(&mgr, s_http_port, ev_handler); if (nc == NULL) { fprintf(stderr, "Cannot bind to %s\n", s_http_port); exit(1); } // Set up HTTP server parameters mg_set_protocol_http_websocket(nc); s_http_server_opts.document_root = "web_root"; // Set up web root directory if (mg_stat(s_http_server_opts.document_root, &st) != 0) { fprintf(stderr, "%s", "Cannot find web_root directory, exiting\n"); exit(1); } printf("Starting web server on port %s\n", s_http_port); for (;;) { mg_mgr_poll(&mgr, 1000); } mg_mgr_free(&mgr); return 0; }