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