mongoose(WEB服務器) 簡單走讀

最近工做中有遇到使用mongoose做爲WEB服務器在嵌入式設備中的,饒有興趣的拜讀了一下源碼(版本爲6.9),特此分享,若有錯誤還請指正。linux

mongoose做爲一種輕量級的WEB服務器,適合在嵌入式設備中使用。其源碼只有兩個文件:moogoose.c、mongoose.h。web

對於網絡編程學的差的我來講, 還算比較容易理解,以linux環境爲例,從如下幾個方面來介紹。編程

 

1. 數據結構服務器

1.1  mg_mgr是mongoose中進行事件管理的結構體,事件分爲5種類型, 共享同一個回調函數,事件類型經過傳參區分。websocket

#define MG_EV_POLL 0    /* Sent to each connection on each mg_mgr_poll() call */
#define MG_EV_ACCEPT 1  /* New connection accepted. union socket_address * */
#define MG_EV_CONNECT 2 /* connect() succeeded or failed. int *  */
#define MG_EV_RECV 3    /* Data has been received. int *num_bytes */
#define MG_EV_SEND 4    /* Data has been written to a socket. int *num_bytes */
#define MG_EV_CLOSE 5   /* Connection is closed. NULL */
#define MG_EV_TIMER 6   /* now >= conn->ev_timer_time. double * */

 

重要成員:網絡

   active_connections :當前活動的鏈接,若是有多個,則以鏈表形式掛接數據結構

  ctl: broadcast 的socketapp

  ifaces:網絡相關的接口集合,在linux下默認爲socket相關接口 socket

/*
 * Mongoose event manager.
 */
struct mg_mgr {
  struct mg_connection *active_connections;
#if MG_ENABLE_HEXDUMP
  const char *hexdump_file; /* Debug hexdump file path */
#endif
#if MG_ENABLE_BROADCAST
  sock_t ctl[2]; /* Socketpair for mg_broadcast() */
#endif
  void *user_data; /* User data */
  int num_ifaces;
  struct mg_iface **ifaces; /* network interfaces */
  const char *nameserver;   /* DNS server to use */
};

#define MG_SOCKET_IFACE_VTABLE \
{ \
mg_socket_if_init, \
mg_socket_if_free, \
mg_socket_if_add_conn, \
mg_socket_if_remove_conn, \
mg_socket_if_poll, \
mg_socket_if_listen_tcp, \
mg_socket_if_listen_udp, \
mg_socket_if_connect_tcp, \
mg_socket_if_connect_udp, \
mg_socket_if_tcp_send, \
mg_socket_if_udp_send, \
mg_socket_if_recved, \
mg_socket_if_create_conn, \
mg_socket_if_destroy_conn, \
mg_socket_if_sock_set, \
mg_socket_if_get_conn_addr, \
}async

 

1.2  mg_connection是一個具體鏈接實例 

 

 

結構體重要成員:

  1. next、prev:  下、上一個鏈接

  2. mgr:對應的事件管理

  3. sock: 對應的socket

  4. sa: socket的地址

  5.recv_mbuf、send_mbuf : 發送和接受的buffer

  6. proto_handler、handler: 協議的回調函數和事件回調函數

/*
 * Mongoose connection.
 */
struct mg_connection {
  struct mg_connection *next, *prev; /* mg_mgr::active_connections linkage */
  struct mg_connection *listener;    /* Set only for accept()-ed connections */
  struct mg_mgr *mgr;                /* Pointer to containing manager */

  sock_t sock; /* Socket to the remote peer */
  int err;
  union socket_address sa; /* Remote peer address */
  size_t recv_mbuf_limit;  /* Max size of recv buffer */
  struct mbuf recv_mbuf;   /* Received data */
  struct mbuf send_mbuf;   /* Data scheduled for sending */
  time_t last_io_time;     /* Timestamp of the last socket IO */
  double ev_timer_time;    /* Timestamp of the future MG_EV_TIMER */
#if MG_ENABLE_SSL
  void *ssl_if_data; /* SSL library data. */
#endif
  mg_event_handler_t proto_handler; /* Protocol-specific event handler */
  void *proto_data;                 /* Protocol-specific data */
  void (*proto_data_destructor)(void *proto_data);
  mg_event_handler_t handler; /* Event handler function */
  void *user_data;            /* User-specific data */
  union {
    void *v;
    /*
     * the C standard is fussy about fitting function pointers into
     * void pointers, since some archs might have fat pointers for functions.
     */
    mg_event_handler_t f;
  } priv_1;
  void *priv_2;
  void *mgr_data; /* Implementation-specific event manager's data. */
  struct mg_iface *iface;
  unsigned long flags;
/* Flags set by Mongoose */
#define MG_F_LISTENING (1 << 0)          /* This connection is listening */
#define MG_F_UDP (1 << 1)                /* This connection is UDP */
#define MG_F_RESOLVING (1 << 2)          /* Waiting for async resolver */
#define MG_F_CONNECTING (1 << 3)         /* connect() call in progress */
#define MG_F_SSL (1 << 4)                /* SSL is enabled on the connection */
#define MG_F_SSL_HANDSHAKE_DONE (1 << 5) /* SSL hanshake has completed */
#define MG_F_WANT_READ (1 << 6)          /* SSL specific */
#define MG_F_WANT_WRITE (1 << 7)         /* SSL specific */
#define MG_F_IS_WEBSOCKET (1 << 8)       /* Websocket specific */

/* Flags that are settable by user */
#define MG_F_SEND_AND_CLOSE (1 << 10)       /* Push remaining data and close  */
#define MG_F_CLOSE_IMMEDIATELY (1 << 11)    /* Disconnect */
#define MG_F_WEBSOCKET_NO_DEFRAG (1 << 12)  /* Websocket specific */
#define MG_F_DELETE_CHUNK (1 << 13)         /* HTTP specific */
#define MG_F_ENABLE_BROADCAST (1 << 14)     /* Allow broadcast address usage */
#define MG_F_TUN_DO_NOT_RECONNECT (1 << 15) /* Don't reconnect tunnel */

#define MG_F_USER_1 (1 << 20) /* Flags left for application */
#define MG_F_USER_2 (1 << 21)
#define MG_F_USER_3 (1 << 22)
#define MG_F_USER_4 (1 << 23)
#define MG_F_USER_5 (1 << 24)
#define MG_F_USER_6 (1 << 25)
};

 2. 實現方式

整個流程其實很簡單,可分爲如下三步

1.   mg_mgr_init     先對mgr進行初始化,主要是將相關的socket接口函數集合賦值給mgr.ifaces

2.   mg_bind           該步驟主要爲一個mg_connection申請內存,並將事件回調函數ev_handler註冊到該鏈接裏,而且初始化若干個(由網卡數量決定)

  http端口的socket進行監聽

3. mg_mgr_poll      該函數調用mongoose中提供的poll接口:mg_socket_if_poll。在該函數中,對全部初始化的socket進行select操做,

   在退出select的阻塞後,根據read_fd_set, write_fd_set, err_fd_set 進行判斷,將退出阻塞的socket分類,而後進行分類處理。

int main(void) {
  struct mg_mgr mgr;
  struct mg_connection *nc;
  cs_stat_t st;

  mg_mgr_init(&mgr, NULL);
  nc = mg_bind(&mgr, s_http_port, ev_handler);
  if (nc == NULL) {
    fprintf(stderr, "Cannot bind to %s\n", s_http_port);
    exit(1);
  }

  // Set up HTTP server parameters
  mg_set_protocol_http_websocket(nc);
  s_http_server_opts.document_root = "web_root";  // Set up web root directory

  if (mg_stat(s_http_server_opts.document_root, &st) != 0) {
    fprintf(stderr, "%s", "Cannot find web_root directory, exiting\n");
    exit(1);
  }

  printf("Starting web server on port %s\n", s_http_port);
  for (;;) {
    mg_mgr_poll(&mgr, 1000);
  }
  mg_mgr_free(&mgr);

  return 0;
}
相關文章
相關標籤/搜索