Mongoose源碼剖析:數據結構篇

引言

Mongoose中有幾個數據結構扮演着重要的角色,它們分別是:

  1. struct mg_context:保存Mongoose的上下文,幾乎每個函數都有mg_context參數
  2. struct mg_connection:保存HTPP連接信息
  3. struct mg_request_info:保存HTTP請求的信息,這個結構體傳遞給URL處理函數

我之所以現在這裏介紹它,因爲之後的分析工作中要用到它們,如果在讀完本文後還不能很好的理解,請將問題帶到後續文章中或代碼分析中去,你會找到答案的。下面分別介紹它們。本文的主要內容如下:

  1. I、mg_context詳解
  2. II、mg_connection詳解
  3. III、mg_request_info詳解
  4. IV、其他數據結構
  5. V、總結

1、mg_context詳解

mg_context結構體——表示Mongoose的上下文,也稱爲一個實例句柄。它的成員如下:

  1. struct mg_context {  
  2.     int     stop_flag;  /* Should we stop event loop    */ 
  3.     SSL_CTX     *ssl_ctx;   /* SSL context          */ 
  4.  
  5.     FILE        *access_log;    /* Opened access log        */ 
  6.     FILE        *error_log; /* Opened error log     */ 
  7.  
  8.     struct socket   listeners[MAX_LISTENING_SOCKETS];  
  9.     int     num_listeners;  
  10.  
  11.     struct callback callbacks[MAX_CALLBACKS];  
  12.     int     num_callbacks;  
  13.  
  14.     char        *options[NUM_OPTIONS];  /* Configured opions    */ 
  15.     pthread_mutex_t opt_mutex[NUM_OPTIONS]; /* Option protector */ 
  16.  
  17.     int     max_threads;    /* Maximum number of threads    */ 
  18.     int     num_threads;    /* Number of threads        */ 
  19.     int     num_idle;   /* Number of idle threads   */ 
  20.     pthread_mutex_t thr_mutex;  /* Protects (max|num)_threads   */ 
  21.     pthread_cond_t  thr_cond;  
  22.     pthread_mutex_t bind_mutex; /* Protects bind operations */ 
  23.  
  24.     struct socket   queue[20];  /* Accepted sockets     */ 
  25.     int     sq_head;    /* Head of the socket queue */ 
  26.     int     sq_tail;    /* Tail of the socket queue */ 
  27.     pthread_cond_t  empty_cond; /* Socket queue empty condvar   */ 
  28.     pthread_cond_t  full_cond;  /* Socket queue full condvar    */ 
  29.  
  30.     mg_spcb_t   ssl_password_callback;  
  31.     mg_callback_t   log_callback;  
  32. }; 

這個結構體在mg_start()中創建和初始化,其它函數大部分都會用它。因此mg_start()應該首先被調用。它非常重要,幾乎所有的函數都要用到它。

1)、stop_flag表示是否應該停止的標記,它有三個可能的值0、1、2。 stop_flag=0表示 不應該停止,這是初始值;stop_flag=1表示停止,在mg_stop()函數中的一開始設置stop_flag=1,這會觸發mg_fini(),且在mg_stop()中會一直等待mg_fini執行完成;stop_flag=2用於通知mg_stop(),mg_fini已經執行完成,stop_flag=2在mg_fini函數中的末尾設置。

2)、ssl_ctx是結構體ssl_ctx_st的實例,它來自OpenSSL開源項目,作者把它放到這裏的原因是使其獨立於OpenSSL的源碼安裝,這樣只有系統上面安裝有SSL庫,mongoose+SSL就能編譯通過。

3)、access_log、error_log很明顯是指向訪問日誌文件、錯誤日誌文件。

4)、listeners數組存儲mongoose建立的多個web server,每個web server都是listeners數組中的一個元素。例如,一個服務器可以分別在端口8080、8888建立web server,這樣8080端口的那個server是listerns數組中的一個元素,8888端口的那個server也是listeners數組中的一個元素。換句話說,listeners數組表示web server的socket地址。num_listeners表示listeners數組的元素個數。

5)、callbacks是結構體callback的數組,而callback本身是一個結構體,包含幾個回調句柄。num_callbacks是callbacks數組元素的個數。

6)、options數組,是用於存儲配置選項的,例如端口號、工作目錄等等。opt_mutext對配置進行操作的互斥變量。

7)、max_threads表示允許的最大線程數量、num_threads表示當前的線程數量、num_idle表示空閒的線程數量。之所以會有空閒進程,是因爲當創建一個線程處理連接請求之後,它會保持一段時間空閒而不是直接銷燬。如果這裏再用新的連接到來或等待隊列中有需要處理的連接,空閒進程會被分配去處理。

8)、thr_mutex、thr_cond、bind_mutex是用於互斥信號量和條件變量。

9)、queue[20]隊列數組存儲client的連接請求,每個元素都是client的socket。sq_head、sq_tail分別是隊列頭、尾用於操作隊列queue。empty_cond、full_cond分別表示隊列是否爲空、滿的條件變量。

10)、ssl_password_callback和log_callback都是函數指針,分別指向SSL密碼處理函數、log處理函數。他們原型是:

  1. /*  
  2.  * Register SSL password handler.  
  3.  * This is needed only if SSL certificate asks for a password. Instead of  
  4.  * prompting for a password on a console a specified function will be called.  
  5.  */ 
  6. typedef int (*mg_spcb_t)(char *buf, int num, int w, void *key);  
  7.  
  8. /*  
  9.  * User-defined callback function prototype for URI handling, error handling,  
  10.  * or logging server messages.  
  11.  */ 
  12. typedef void (*mg_callback_t)(struct mg_connection *,  
  13.         const struct mg_request_info *info, void *user_data); 

是上面講了那麼多感覺挺亂的,下面用張圖片來形象表示一下:

Mongoose源碼剖析:數據結構篇

圖1、mg_context結構體的成員

2、mg_connection詳解

故名思意,這個結構體用戶保存client的連接信息。它的成員如下:

  1. /*  
  2.  * Client connection.  
  3.  */ 
  4. struct mg_connection {  
  5.     struct mg_request_info  request_info;  
  6.     struct mg_context *ctx;     /* Mongoose context we belong to*/ 
  7.     SSL     *ssl;       /* SSL descriptor       */ 
  8.     struct socket   client;     /* Connected client     */ 
  9.     time_t      birth_time; /* Time connection was accepted */ 
  10.     bool_t      free_post_data; /* post_data was malloc-ed  */ 
  11.     bool_t      embedded_auth;  /* Used for authorization   */ 
  12.     uint64_t    num_bytes_sent; /* Total bytes sent to client   */ 
  13. }; 

上面的字段意思都很明顯這裏就不一一闡述了。可以看出, 每個連接都保存了一個Mongoose上下文(mg_context * ctx),這個很重要,對連接請求進行處理時都會用到。這裏也可以看出mg_context相當於一個實例句柄。

結構體mg_request_info用於保存每個請求的信息,例如,當我打開博客主頁http://skynet.blog.51cto.com/ 的時候,會發出一個請求信息,包括請求的方法是POST還是GET等、uri即http://skynet.blog.51cto.com/ 、http版本、還有一些http頭信息等等。關於結構體mg_request_info的詳細信息參見下一小節。

mg_connection的圖像表示如下:

Mongoose源碼剖析:數據結構篇

圖2、mg_connection結構體的成員

3、mg_request_info詳解

這個結構體保存每次client發送請求,即是一個HTTP請求報文信息。而我們知道HTTP的請求報文信息的格式如下:

Mongoose源碼剖析:數據結構篇

  圖3、HTTP請求的格式

根據這個信息,可以更好地理解mg_request_info。mg_request_info結構定義如下:

  1. /*  
  2.  * This structure contains full information about the HTTP request.  
  3.  * It is passed to the user-specified callback function as a parameter.  
  4.  */ 
  5. struct mg_request_info {  
  6.     char    *request_method;    /* "GET", "POST", etc   */ 
  7.     char    *uri;           /* Normalized URI   */ 
  8.     char    *query_string;      /* \0 - terminated  */ 
  9.     char    *post_data;     /* POST data buffer */ 
  10.     char    *remote_user;       /* Authenticated user   */ 
  11.     long    remote_ip;      /* Client's IP address  */ 
  12.     int remote_port;        /* Client's port    */ 
  13.     int post_data_len;      /* POST buffer length   */ 
  14.     int http_version_major;  
  15.     int http_version_minor;  
  16.     int status_code;        /* HTTP status code */ 
  17.     int num_headers;        /* Number of headers    */ 
  18.     struct mg_header {  
  19.         char    *name;      /* HTTP header name */ 
  20.         char    *value;     /* HTTP header value    */ 
  21.     } http_headers[64];     /* Maximum 64 headers   */ 
  22. }; 

從字段都能夠故名思意,這裏就不再闡述了。

4、其他數據結構 

除了上面3個主要的數據結構,還有其它一些數據也默默地貢獻着自己的一份力量。作爲一個整體,少了它們Mongoose也只能淪爲廢物。下面我就列舉幾個:

  1. /*  
  2.  * Structure used by mg_stat() function. Uses 64 bit file length.  
  3.  */ 
  4. struct mgstat {  
  5.     bool_t      is_directory;   /* Directory marker     */ 
  6.     uint64_t    size;       /* File size            */ 
  7.     time_t      mtime;      /* Modification time        */ 
  8. };  
  9.  
  10. struct mg_option {  
  11.     const char  *name;  
  12.     const char  *description;  
  13.     const char  *default_value;  
  14.     int     index;  
  15.     bool_t (*setter)(struct mg_context *, const char *);  
  16. };  
  17. /*  
  18.  * Structure used to describe listening socket, or socket which was  
  19.  * accept()-ed by the master thread and queued for future handling  
  20.  * by the worker thread.  
  21.  */ 
  22. struct socket {  
  23.     SOCKET      sock;       /* Listening socket     */ 
  24.     struct usa  lsa;        /* Local socket address     */ 
  25.     struct usa  rsa;        /* Remote socket address    */ 
  26.     bool_t      is_ssl;     /* Is socket SSL-ed     */ 
  27. };  
  28. /*  
  29.  * Unified socket address. For IPv6 support, add IPv6 address structure  
  30.  * in the union u.  
  31.  */ 
  32. struct usa {  
  33.     socklen_t len;  
  34.     union {  
  35.         struct sockaddr sa;  
  36.         struct sockaddr_in sin;  
  37.     } u;  
  38. };  
  39.  
  40. /*  
  41.  * Specifies a string (chunk of memory).  
  42.  * Used to traverse comma separated lists of options.  
  43.  */ 
  44. struct vec {  
  45.     const char  *ptr;  
  46.     size_t      len;  
  47. };  
  48. /*  
  49.  * Dynamically loaded SSL functionality  
  50.  */ 
  51. struct ssl_func {  
  52.     const char  *name;      /* SSL function name    */ 
  53.     void        (*ptr)(void);   /* Function pointer */ 
  54. }; 

5、總結

至此,我們介紹了Mongoose中使用的一些數據結構,搞清楚這些數據結構對整個項目的理解非常重要。它們遍佈在項目的每個角落(雖然項目比較小)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章