Unix網絡編程——Mongoose開源項目源碼解讀(1):概述

參考資料:https://docs.cesanta.com/mongoose/master/
源碼下載地址:https://github.com/cesanta/mongoose
當前最新版本號:Mongoose 6.7
聲明:本文章參照Mongoose網站說明,可視爲其簡略翻譯,只做學習記錄使用,由於能力有限,不保證一定準確無誤。如需轉載請註明出處,謝謝~

簡述:
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 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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章