Mongoose的入门
前言
Mongoose 基于C的web库,这篇文章记录了Mongoose的基本知识点,在下小白一枚,如有不对的地方请多多指正。
Mongoose是什么?
Mongoose是一个用C语言编写的网络库,可以用于嵌入式网络编程。它为TCP、UDP、HTTP、WebSocket、CoAP、MQTT等协议实现了事件驱动的非阻塞API,用于客户机和服务器模式。
Mongoose有三种基本数据结构:
(1)mg_mgr保存所有活动连接的事件管理器;
(2)mg_connection描述连接;
(3)mbuf描述接收/发送数据的缓冲区;
连接(connections):由结构体mg_connection结构体描述,连接方式包括:listening(由mg_bind()函数创建),outbound(由mg_connect()函数创建),inbound(监听接收的连接);
实现
Mongoose程序是事件驱动应用程序,一般通过以下步骤实现:
(1)构建事件管理器
struct mg_mgr mgr; mg_mgr_init(&mgr, NULL);
(2)创建连接(服务器程序的监听连接)
struct mg_connection *c = mg_bind(&mgr, "80", ev_handler_function);
mg_set_protocol_http_websocket(c);
(3)创建事件循环
while(true) {
mg_mgr_poll(&mgr, 1000);
}//mg_mgr_poll() 遍历所有socket,接受新连接,发送和接收数据,关闭连接并调用事件处理函数
内存缓存和数据的收发
每个连接包含一个发送(struct mg_connection::send_mbuf )和一个缓存( struct mg_connection::recv_mbuf)。
当数据接收后,Mongoose将接收到的数据加到recv_mbuf后面,并触发一个MG_EV_RECV事件。
用户通过输出函数(如mg_send() / mg_printf())将数据发送回去,输出函数将数据追加到send_mbuf。
成功地将数据写到socket后,它将丢弃struct mg_connection::send_mbuf 里的数据,并发送一个MG_EV_SEND事件。
当连接关闭后,发送一个MG_EV_CLOSE事件。
事件处理函数
每个连接都有一个与之关联的事件处理函数。这些函数必须由用户实现。事件处理器是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 */
...
}
}
struct mg_connection *nc : 接收事件的连接。int ev : 时间编号,定义在mongoose.h。
例如,当数据来自于一个inbound连接,ev就是MG_EV_RECV。
void *ev_data 指针指向event-specific事件,确切描述每个事件的意义(不同的事件有不同的意义,当发生MG_EV_RECV事件,ev_data是int *指针,指向从远程接收并保存到接收IO缓冲区中的字节数)。当发生Protocol-specific事件,ev_data指向保存protocol-specific信息的结构体。
事件
Mongoose接收传入连接、读取和写入数据,并在适当时为每个连接调用指定的事件处理程序。
典型的事件顺序有:
(1)出站连接:MG_EV_CONNECT -> (MG_EV_RECV, MG_EV_SEND, MG_EV_POLL ...) -> MG_EV_CLOSE,
(2)入站连接:MG_EV_ACCEPT -> (MG_EV_RECV, MG_EV_SEND, MG_EV_POLL ...) -> MG_EV_CLOSE
Mongoose触发的核心事件列表:
事件 | 功能 |
---|---|
MG_EV_ACCEPT | 当监听连接接收到一个新服务器连接时触发。void *ev_data是远程端的union socket_address |
MG_EV_CONNECT | 当mg_connect()创建了一个新出站连接时触发。void *ev_data是int *success。当success是0,则连接已经建立,否则包含一个错误码。 |
MG_EV_RECV | 新数据接收并追加到recv_mbuf结尾时触发。void *ev_data是int *num_received_bytes。时间处理器在nc->recv_mbuf检查接收数据,调用mbuf_remove()丢弃已处理的数据。请查看连接标识nc->flags(see struct mg_connection),并通过输出函数(如mg_send())写数据到远程端。 |
警告:Mongoose使用realloc()展开接收缓冲区,用户有责任从接收缓冲区的开头丢弃已处理的数据,请注意上面示例中的mbuf_remove()调用。
MG_EV_SEND: Mongoose已经写数据到远程,并且已经丢弃写入到mg_connection::send_mbuf的数据。void *ev_data是int *num_sent_bytes。
注意:Mongoose输出函数仅追加数据到mg_connection::send_mbuf。它们不做任何socket的写入操作。一个真实的IO是通过mg_mgr_poll()完成的。一个MG_EV_SEND事件仅仅是一个关于IO完成的通知。
MG_EV_POLL:在每次调用mg_mgr_poll()时发送到所有连接。该事件被用于做任何事情,例如,检查某个超时是否已过期并关闭连接或发送心跳消息等。
MG_EV_TIMER: 当mg_set_timer()调用后,发送到连接
TCP服务器示例
#include "mongoose.h" // Include Mongoose API definitions
// Define an event handler function
static 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;
}