引言
Mongoose中有几个数据结构扮演着重要的角色,它们分别是:
- struct mg_context:保存Mongoose的上下文,几乎每个函数都有mg_context参数
- struct mg_connection:保存HTPP连接信息
- struct mg_request_info:保存HTTP请求的信息,这个结构体传递给URL处理函数
我之所以现在这里介绍它,因为之后的分析工作中要用到它们,如果在读完本文后还不能很好的理解,请将问题带到后续文章中或代码分析中去,你会找到答案的。下面分别介绍它们。本文的主要内容如下:
- 1、mg_context详解
- 2、mg_connection详解
- 3、mg_request_info详解
- 4、其他数据结构
- 5、总结
1、mg_context详解
mg_context结构体——表示Mongoose的上下文,也称为一个实例句柄。它的成员如下:
struct mg_context { int stop_flag; /* Should we stop event loop */ SSL_CTX *ssl_ctx; /* SSL context */ FILE *access_log; /* Opened access log */ FILE *error_log; /* Opened error log */ struct socket listeners[MAX_LISTENING_SOCKETS]; int num_listeners; struct callback callbacks[MAX_CALLBACKS]; int num_callbacks; char *options[NUM_OPTIONS]; /* Configured opions */ pthread_mutex_t opt_mutex[NUM_OPTIONS]; /* Option protector */ int max_threads; /* Maximum number of threads */ int num_threads; /* Number of threads */ int num_idle; /* Number of idle threads */ pthread_mutex_t thr_mutex; /* Protects (max|num)_threads */ pthread_cond_t thr_cond; pthread_mutex_t bind_mutex; /* Protects bind operations */ struct socket queue[20]; /* Accepted sockets */ int sq_head; /* Head of the socket queue */ int sq_tail; /* Tail of the socket queue */ pthread_cond_t empty_cond; /* Socket queue empty condvar */ pthread_cond_t full_cond; /* Socket queue full condvar */ mg_spcb_t ssl_password_callback; mg_callback_t log_callback; };
这个结构体在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处理函数。他们原型是:
/* * Register SSL password handler. * This is needed only if SSL certificate asks for a password. Instead of * prompting for a password on a console a specified function will be called. */ typedef int (*mg_spcb_t)(char *buf, int num, int w, void *key); /* * User-defined callback function prototype for URI handling, error handling, * or logging server messages. */ typedef void (*mg_callback_t)(struct mg_connection *, const struct mg_request_info *info, void *user_data);
是上面讲了那么多感觉挺乱的,下面用张图片来形象表示一下:
图1、mg_context结构体的成员
2、mg_connection详解
故名思意,这个结构体用户保存client的连接信息。它的成员如下:
/* * Client connection. */ struct mg_connection { struct mg_request_info request_info; struct mg_context *ctx; /* Mongoose context we belong to*/ SSL *ssl; /* SSL descriptor */ struct socket client; /* Connected client */ time_t birth_time; /* Time connection was accepted */ bool_t free_post_data; /* post_data was malloc-ed */ bool_t embedded_auth; /* Used for authorization */ uint64_t num_bytes_sent; /* Total bytes sent to client */ };
上面的字段意思都很明显这里就不一一阐述了。可以看出, 每个连接都保存了一个Mongoose上下文(mg_context * ctx),这个很重要,对连接请求进行处理时都会用到。这里也可以看出mg_context相当于一个实例句柄。
结构体mg_request_info用于保存每个请求的信息,例如,当我打开博客主页http://www.cnblogs.com/skynet/的时候,会发出一个请求信息,包括请求的方法是POST还是GET等、uri即http://www.cnblogs.com/skynet/、http版本、还有一些http头信息等等。关于结构体mg_request_info的详细信息参见下一小节。
mg_connection的图像表示如下:
图2、mg_connection结构体的成员
3、mg_request_info详解
这个结构体保存每次client发送请求,即是一个HTTP请求报文信息。而我们知道HTTP的请求报文信息的格式如下:
图3、HTTP请求的格式
根据这个信息,可以更好地理解mg_request_info。mg_request_info结构定义如下:
/* * This structure contains full information about the HTTP request. * It is passed to the user-specified callback function as a parameter. */ struct mg_request_info { char *request_method; /* "GET", "POST", etc */ char *uri; /* Normalized URI */ char *query_string; /* \0 - terminated */ char *post_data; /* POST data buffer */ char *remote_user; /* Authenticated user */ long remote_ip; /* Client's IP address */ int remote_port; /* Client's port */ int post_data_len; /* POST buffer length */ int http_version_major; int http_version_minor; int status_code; /* HTTP status code */ int num_headers; /* Number of headers */ struct mg_header { char *name; /* HTTP header name */ char *value; /* HTTP header value */ } http_headers[64]; /* Maximum 64 headers */ };
从字段都能够故名思意,这里就不再阐述了。
4、其他数据结构
除了上面3个主要的数据结构,还有其它一些数据也默默地贡献着自己的一份力量。作为一个整体,少了它们Mongoose也只能沦为废物。下面我就列举几个:
/* * Structure used by mg_stat() function. Uses 64 bit file length. */ struct mgstat { bool_t is_directory; /* Directory marker */ uint64_t size; /* File size */ time_t mtime; /* Modification time */ }; struct mg_option { const char *name; const char *description; const char *default_value; int index; bool_t (*setter)(struct mg_context *, const char *); }; /* * Structure used to describe listening socket, or socket which was * accept()-ed by the master thread and queued for future handling * by the worker thread. */ struct socket { SOCKET sock; /* Listening socket */ struct usa lsa; /* Local socket address */ struct usa rsa; /* Remote socket address */ bool_t is_ssl; /* Is socket SSL-ed */ }; /* * Unified socket address. For IPv6 support, add IPv6 address structure * in the union u. */ struct usa { socklen_t len; union { struct sockaddr sa; struct sockaddr_in sin; } u; }; /* * Specifies a string (chunk of memory). * Used to traverse comma separated lists of options. */ struct vec { const char *ptr; size_t len; }; /* * Dynamically loaded SSL functionality */ struct ssl_func { const char *name; /* SSL function name */ void (*ptr)(void); /* Function pointer */ };
5、总结
至此,我们介绍了Mongoose中使用的一些数据结构,搞清楚这些数据结构对整个项目的理解非常重要。它们遍布在项目的每个角落(虽然项目比较小)。