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中使用的一些数据结构,搞清楚这些数据结构对整个项目的理解非常重要。它们遍布在项目的每个角落(虽然项目比较小)。







     本文转自Saylor87 51CTO博客,原文链接:http://blog.51cto.com/skynet/363324,如需转载请自行联系原作者






相关文章
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
1087 9
|
7月前
|
存储 安全 Java
Java 集合面试题从数据结构到 HashMap 源码剖析详解及长尾考点梳理
本文深入解析Java集合框架,涵盖基础概念、常见集合类型及HashMap的底层数据结构与源码实现。从Collection、Map到Iterator接口,逐一剖析其特性与应用场景。重点解读HashMap在JDK1.7与1.8中的数据结构演变,包括数组+链表+红黑树优化,以及put方法和扩容机制的实现细节。结合订单管理与用户权限管理等实际案例,展示集合框架的应用价值,助你全面掌握相关知识,轻松应对面试与开发需求。
363 3
|
10月前
|
存储 自然语言处理 数据库
【数据结构进阶】AVL树深度剖析 + 实现(附源码)
在深入探讨了AVL树的原理和实现后,我们不难发现,这种数据结构不仅优雅地解决了传统二叉搜索树可能面临的性能退化问题,还通过其独特的平衡机制,确保了在任何情况下都能提供稳定且高效的查找、插入和删除操作。
779 19
|
10月前
|
数据库 C++
【数据结构进阶】红黑树超详解 + 实现(附源码)
本文深入探讨了红黑树的实现原理与特性。红黑树是一种自平衡二叉搜索树,通过节点着色(红/黑)和特定规则,确保树的高度接近平衡,从而实现高效的插入、删除和查找操作。相比AVL树,红黑树允许一定程度的不平衡,减少了旋转调整次数,提升了动态操作性能。文章详细解析了红黑树的性质、插入时的平衡调整(变色与旋转)、查找逻辑以及合法性检查,并提供了完整的C++代码实现。红黑树在操作系统和数据库中广泛应用,其设计兼顾效率与复杂性的平衡。
2076 3
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
675 16
|
C语言
【数据结构】二叉树(c语言)(附源码)
本文介绍了如何使用链式结构实现二叉树的基本功能,包括前序、中序、后序和层序遍历,统计节点个数和树的高度,查找节点,判断是否为完全二叉树,以及销毁二叉树。通过手动创建一棵二叉树,详细讲解了每个功能的实现方法和代码示例,帮助读者深入理解递归和数据结构的应用。
1166 8
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
1261 4
|
存储 C语言
【数据结构】顺序表(c语言实现)(附源码)
本文介绍了线性表和顺序表的基本概念及其实现。线性表是一种有限序列,常见的线性表有顺序表、链表、栈、队列等。顺序表是一种基于连续内存地址存储数据的数据结构,其底层逻辑是数组。文章详细讲解了静态顺序表和动态顺序表的区别,并重点介绍了动态顺序表的实现,包括初始化、销毁、打印、增删查改等操作。最后,文章总结了顺序表的时间复杂度和局限性,并预告了后续关于链表的内容。
425 3
|
Java C++ 索引
让星星⭐月亮告诉你,LinkedList和ArrayList底层数据结构及方法源码说明
`LinkedList` 和 `ArrayList` 是 Java 中两种常见的列表实现。`LinkedList` 基于双向链表,适合频繁的插入和删除操作,但按索引访问元素效率较低。`ArrayList` 基于动态数组,支持快速随机访问,但在中间位置插入或删除元素时性能较差。两者均实现了 `List` 接口,`LinkedList` 还额外实现了 `Deque` 接口,提供了更多队列操作。
286 3
|
C语言
【数据结构】双向带头循环链表(c语言)(附源码)
本文介绍了双向带头循环链表的概念和实现。双向带头循环链表具有三个关键点:双向、带头和循环。与单链表相比,它的头插、尾插、头删、尾删等操作的时间复杂度均为O(1),提高了运行效率。文章详细讲解了链表的结构定义、方法声明和实现,包括创建新节点、初始化、打印、判断是否为空、插入和删除节点等操作。最后提供了完整的代码示例。
444 0

热门文章

最新文章