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
