Mongoose是一个用C编写的网络库。它为客户端和服务器模式实现TCP,UDP,HTTP,WebSocket,CoAP,MQTT的事件驱动的非阻塞API。
设计理念:
Mongoose有三个基本的数据结构:
struct mg_mgr 是一个事件管理器,保存所有活动的连接 struct mg_connection 描述一个连接 struct mbuf 描述数据缓冲区(接收或发送的数据)
连接可能是listening,inbound或outbound。通过调用mg_connect()
创建outbound连接。listening连接是由mg_bind()
创建的。inbound连接是通过listening连接接受后的连接。每个连接由struct mg_connection
结构描述,它具有许多字段,如套接字,事件处理函数,发送/接收缓冲区,标志等。
使用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. 通过调用循环创建一个事件mg_mgr_poll()
循环:
for (;;) { mg_mgr_poll(&mgr, 1000); }
mg_mgr_poll()
迭代所有套接字,接受新连接,发送和接收数据,关闭连接并调用相应事件的事件处理函数。
内存缓冲区
每个连接有一个发送和接收缓冲区,分别是struct mg_connection::send_mbuf 和
struct mg_connection::recv_mbuf
。当数据到达时,Mongoose将收到的数据附加到recv_mbuf
并触发MG_EV_RECV
事件。用户可以通过调用输出函数之一来发送数据,如 mg_send()
或mg_printf()
。输出功能将数据附加到send_mbuf
。当Mongoose成功将数据写入套接字时,它将丢弃数据 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
:该指针指向事件特定的数据,它对于不同的事件具有不同的含义。例如,对于MG_EV_RECV
事件,ev_data
是int *
指向从远程对等体接收并保存到接收IO缓冲区中的字节数的指针。ev_data
每个事件描述的具体含义 。特定于协议的事件通常ev_data
指向具有协议特定信息的结构。
注意:对于应用程序特定数据,struct mg_connection
具有void *user_data
占位符。Mongoose不使用该指针。事件处理程序可以存储任何类型的信息。
活动
Mongoose接受传入连接,读取和写入数据,并在适当时调用每个连接的指定事件处理程序。这是一个典型的事件序列:
outbound 连接: MG_EV_CONNECT -> (MG_EV_RECV, MG_EV_SEND, MG_EV_POLL...) -> MG_EV_CLOSE
inbound 连接: MG_EV_ACCEPT -> (MG_EV_RECV, MG_EV_SEND, MG_EV_POLL ...) -> MG_EV_CLOSE
以下是由Mongoose触发的核心事件列表(请注意,除了核心事件之外,每个协议都会触发特定于协议的事件):
-
MG_EV_ACCEPT
:当listening连接接受新的服务器连接时发送。void *ev_data
是union socket_address
远程对等体。 -
MG_EV_CONNECT:
当由mg_connect()
创建新的出站连接时发送(无论失败或成功)。void *ev_data
是int *success
。如果success
为0,则连接已建立,否则包含错误代码。参见mg_connect_opt()
代码示例的功能。 -
MG_EV_RECV
:收到新数据并将其附加到recv_mbuf
。void *ev_data
是int *num_received_bytes
。通常,事件处理程序应检查接收到的数据nc->recv_mbuf
,通过调用mbuf_remove()来丢弃已处理的数据,必要 时设置连接标志nc->flags
(见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
。他们不做任何套接字写。实际的IO由mg_mgr_poll()完成。一个MG_EV_SEND
事件是关于IO已经完成的通知。 -
MG_EV_POLL
:发送到每个调用的所有连接mg_mgr_poll()
。此事件可用于执行任何内务处理,例如检查某个超时是否过期并关闭连接或发送心跳消息等。 -
MG_EV_TIMER
发送到连接如果mg_set_timer()
被调用。
连接标志
每个连接都有一个flags
位字段。一些标志由Mongoose设置,例如,如果用户使用udp://1.2.3.4:5678
地址创建出站UDP连接,Mongoose将MG_F_UDP
为该连接设置一个标志。其他标志仅由用户事件处理程序设置,以告诉Mongoose如何行为。以下是由事件处理程序设置的连接标志列表:
MG_F_FINISHED_SENDING_DATA
告诉Mongoose所有的数据已被追加到send_mbuf
。一旦Mongoose将其发送到套接字,连接将被关闭。MG_F_BUFFER_BUT_DONT_SEND
告诉Mongoose将数据附加到send_mbuf
但是发送它,因为数据将被稍后修改,然后通过清除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握手完成后设置。MG_F_CONNECTING
当mg_connect()
连接处于连接状态, 但连接尚未完成后设置。MG_F_LISTENING
设置为所有侦听连接。MG_F_UDP
如果连接是UDP,请设置。MG_F_IS_WEBSOCKET
设置如果连接是WebSocket连接。MG_F_WEBSOCKET_NO_DEFRAG
如果用户想要关闭自动WebSocket框架碎片整理,应由用户设置。
构建选项
Mongoose源代码包含在包含所有支持的协议(模块)的功能的单个.c文件中。可以在编译时禁用模块,从而减少可执行文件的大小。这可以通过设置预处理程序标志来实现。此外,一些预处理器标志可用于调整内部Mongoose参数。
要在编译期间设置预处理器标志,请使用-D <PREPROCESSOR_FLAG>
编译器选项。例如,要禁用MQTT和CoAP,请编译my_app.c
这样的应用程序(假定为UNIX系统):
$ cc my_app.c mongoose.c -D MG_DISABLE_MQTT -D MG_DISABLE_COAP