简述:
Mongoose前身为shttpd,使用标准C/C++编写而成,转为嵌入式设备设计的,支持跨平台的网络服务器库。Mongoose实现了非阻塞式事件驱动API,支持TCP, UDP, HTTP, WebSocket, CoAP, MQTT。
Mongoose的三个基本结构体:
struct mg_mgr; // 持有所有活动的连接的事件管理器struct mg_connection ; // 用于连接的套接字状态的描述struct mbuf ; // 用于接收和发送数据缓存的描述
声明与初始化事件管理器
struct mg_mgr mgr; mg_mgr_init(&mgr, NULL);
创建连接,比如创建一个服务器端的监听套接字
struct mg_connection *c = mg_bind(&mgr, "80", ev_handler_function); mg_set_protocol_http_websocket(c);
轮询处理:遍历所有套接字,接收新的连接请求,发送与接受数据,关闭连接,事件处理等
for (;;) { mg_mgr_poll(&mgr, 1000); }
收发缓冲区
每个连接都有自己的收发缓冲区struct mbuf,当Mongoose 接收到数据时数据被追加到接收缓冲区并触发一个MG_EV_RECV 事件。当需要发送数据时,只需要使用 mg_send()或者mg_printf()将数据追加到发送缓冲区,当数据发送成功,Mongoose 将数据从发送缓冲区删除并发送一个MG_EV_SEND事件。当连接关闭时,触发MG_EV_CLOSE事件。以下为mbuf的定义:
/* Memory buffer descriptor */struct mbuf { char *buf; /* Buffer pointer */ size_t len; /* Data length. Data is located between offset 0 and len. */ size_t size; /* Buffer size allocated by realloc(1). Must be >= len */};
事件及事件处理函数
Mongoose 为连接、读写、关闭等都定义了事件,每个连接都有与其关联的事件处理函数——该函数由用户自身实现,该函数的原型如下:
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { switch (ev) { /* Event handler code that defines behavior of the connection */ ... } }
典型的事件序列如下:
- 对于客户端:MG_EV_CONNECT -> (MG_EV_RECV, MG_EV_SEND, MG_EV_POLL …) -> MG_EV_CLOSE
- 对于服务端: MG_EV_ACCEPT -> (MG_EV_RECV, MG_EV_SEND, MG_EV_POLL …) -> MG_EV_CLOSE
以下为Mongoose的核心事件:
- MG_EV_ACCEPT:当accept到一个新的连接请求时触发该事件,ev_data 为socket_address 联合体
- MG_EV_CONNECT:当使用mg_connect() 创建连接套接字时触发该事件,ev_data 为int *success,success为0表示成功,否则为失败的errno
- MG_EV_RECV:接收到新的数据,void *ev_data 是接收到的字节数,接收到应该使用recv_mbuf来获取数据,使用mg_send()发送数据。Mongoose 使用realloc来扩展接收缓冲区,但是需要由用户删除已接收的数据——比如mbuf_remove()。
- MG_EV_SEND:Mongoose 已经将(int *)ev_data 的数据写到了远端并将数据从send_mbuf中删除。mg_send()并不发送数据,只是将数据追加到缓冲区,正真的IO操作由mg_mgr_poll()完成。
- MG_EV_POLL:该事件被发送给所有的已连接套接字,它可以被用来作任何持续性的事务,比如检查某个连接是否已经超时或者关闭、过期,或者用来发送心跳消息。
- MG_EV_TIMER:当mg_set_timer() 被调用时被用来发送给指定的connection
连接标记位
每个连接都有自己的标记位,比如当创建一个UDP连接时,Mongoose 将为这个连接的标记为设置为MG_F_UDP。
以下标记为用户设定:
- MG_F_FINISHED_SENDING_DATA:告诉Mongoose 数据已经全部存放到了发送缓冲区,当Mongoose 将数据发送完毕时,主动关闭连接。
- MG_F_BUFFER_BUT_DONT_SEND :告诉Mongoose 只将数据追加到缓冲区但是不要发送数据,因为数据之后可能会被修改,当MG_F_BUFFER_BUT_DONT_SEND标记位被清除时数据再被发送
- MG_F_CLOSE_IMMEDIATELY :告诉Mongoose 立马关闭连接,一般在出错的时候设置
- MG_F_USER_1, MG_F_USER_2, MG_F_USER_3, MG_F_USER_4:用户自定义,用来存放应用的指定状态
以下标记由Mongoose 设定:
- MG_F_SSL_HANDSHAKE_DONE SSL:只有在SSL连接中才会设置,当SSL的握手完成时设定
- MG_F_CONNECTING:在调用mg_connect() 后但是连接还没有完成时设置
- MG_F_LISTENING:为所有监听套接字设置
- MG_F_UDP:如果是UDP协议则设置
- MG_F_IS_WEBSOCKET:如果是网络套接字则设置
- MG_F_WEBSOCKET_NO_DEFRAG:由用户希望关闭自动的websocket框架碎片整理时设置
编译选项:
Mongoose 源代码由单一的c文件构成,Mongoose 所支持的协议都由它提供。在编译时Mongoose 可以去除不需要的功能以减小执行文件的大小。比如可以使用-D MG_DISABLE_MQTT -D MG_DISABLE_COAP去除代码中的MQTT和CoAP的支持代码。
linux下的编译样例:
cc my_app.c mongoose.c -D MG_DISABLE_MQTT -D MG_DISABLE_COAP
Mongoose 的使用样例:
1.将mongoose.c 和mongoose.h拷贝到你的工程目录下
2.使用mongoose提供的API编写工程,例如my_app.c
3.编译工程:cc my_app.c mongoose.c
#include "mongoose.h" // Include Mongoose API definitions// Define an event handler functionstatic void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { struct mbuf *io = &nc->recv_mbuf; switch (ev) { case MG_EV_RECV: // This event handler implements simple TCP echo server mg_send(nc, io->buf, io->len); // Echo received data back mbuf_remove(io, io->len); // Discard data from recv buffer break; default: break; } }int main(void) { struct mg_mgr mgr; mg_mgr_init(&mgr, NULL); // Initialize event manager object // Note that many connections can be added to a single event manager // Connections can be created at any point, e.g. in event handler function mg_bind(&mgr, "1234", ev_handler); // Create listening connection and add it to the event manager for (;;) { // Start infinite event loop mg_mgr_poll(&mgr, 1000); } mg_mgr_free(&mgr); return 0; }