Mongoose源码剖析:数据结构篇

引言

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

时间: 2024-10-05 22:10:36

Mongoose源码剖析:数据结构篇的相关文章

Mongoose源码剖析:核心处理模块

引言 前面我们介绍了 Mongoose所有的几个主要的数据结构mg_context.mg_connection.mg_request_info,还有Mongoose的 生命主线.有了这些基础就可以来看看Mongoose的核心处理工作是怎样的.如果你还没有阅读前面的文章,你可以通过下面的隧道直通: Mongoose源码剖析:外篇之web服务器 Mongoose源码剖析:Introduction and Installation Mongoose源码剖析:数据结构篇 Mongoose源码剖析:mon

Mongoose源码剖析:外篇之web服务器

引言 在深入Mongoose源码剖析之前,我们应该清楚web服务器是什么?它提供什么服务?怎样提供服务?使用什么协议?客户端如何唯一标识web服务器的资源?下面我们抛开Mongoose,来介绍一个web服务的这些通性. web服务器:通常是指一个计算机程序(web服务器是什么?),在World Wide Web上提供诸如web页面的服务(提供什么服务?),使用HyperText Transfer Protocol(HTTP)(使用什么协议?).当然web服务器也可以指运行这个程序的计算机或虚拟机

Mongoose源码剖析:Introduction and Installation

引言 要剖析Mongoose的源码,首先你得知道它的一些基本情况和特性.并去使用它.本文就是介绍Mongoose是个什么东西?及如何安装和使用?这里假设你知道什么web服务器软件.web服务器使用什么协议.怎么提供服务等,如果你还不知道建议首先阅读:<Mongoose源码剖析:外篇之web服务器>. 本文主要内容如下: 1.Mongoose介绍 2.Mongoose的编译和使用 3.Makefile的简单分析 4.如何将Mongoose嵌入到应用程序中 1.Mongoose介绍 Mongoos

Mongoose源码剖析:mongoose的工作模型

引言 我看一个项目的时候,比较喜欢首先看它的架构和设计.因为这样在研读源码的时候,有一个指导作用,不会迷失于具体细节,并能够引导我如何去将点串成线,将线串成面.而且一个软件怎么样,很大程度上取决于它采用的架构. 本文主要介绍Mongoose的工作模型,及根据这个模型将代码大致串起来,找出主线.内容框架如下: 1.线程模型 2.从程序入口着手 3.Mongoose的生命旅程 1.线程模型 Mongoose 采用了一个自适应的线程池的模型.有一个主线程(master thread)用于打开配置端口和

菜鸟nginx源码剖析 框架篇(一) 从main函数看nginx启动流程(转)

俗话说的好,牵牛要牵牛鼻子 驾车顶牛,处理复杂的东西,只要抓住重点,才能理清脉络,不至于深陷其中,不能自拔.对复杂的nginx而言,main函数就是"牛之鼻",只要能理清main函数,就一定能理解其中的奥秘,下面我们就一起来研究一下nginx的main函数. 1.nginx的main函数解读 nginx启动显然是由main函数驱动的,main函数在在core/nginx.c文件中,其源代码解析如下,涉及到的数据结构在本节仅指出其作用,将在第二节中详细解释. nginx main函数的流

Java集合源码剖析:HashMap源码剖析

HashMap简介 HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长. HashMap是非线程安全的,只是用于单线程环境下,多线程环境下可以采用concurrent并发包下的concurrentHashMap. HashMap 实现了Serializable接口,因此它支持序列化,实现了Cloneable接口,能被克隆. HashMap源码剖析 HashMap的源码如下(加入了比较详细的注释): pac

Java集合源码剖析:Vector源码剖析

Vector简介 Vector也是基于数组实现的,是一个动态数组,其容量能自动增长. LinkedList是JDK1.0引入了,它的很多实现方法都加入了同步语句,因此是线程安全的(其实也只是相对安全,有些时候还是要加入同步语句来保证线程的安全),可以用于多线程环境. LinkedList没有丝线Serializable接口,因此它不支持序列化,实现了Cloneable接口,能被克隆,实现了RandomAccess接口,支持快速随机访问. Vector源码剖析 Vector的源码如下(加入了比较详

Java集合源码剖析:ArrayList源码剖析

ArrayList简介 ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存. ArrayList不是线程安全的,只能用在单线程环境下,多线程环境下可以考虑用Collections.synchronizedList(List l)函数返回一个线程安全的ArrayList类,也可以使用concurrent并发包下的CopyOnWriteArrayList类. ArrayList实现了Serializable接口,因此它支持序列化,能够通过

Java集合源码剖析:LinkedList源码剖析

LinkedList简介 LinkedList是基于双向循环链表(从源码中可以很容易看出)实现的,除了可以当做链表来操作外,它还可以当做栈.队列和双端队列来使用. LinkedList同样是非线程安全的,只在单线程下适合使用. LinkedList实现了Serializable接口,因此它支持序列化,能够通过序列化传输,实现了Cloneable接口,能被克隆. LinkedList源码剖析 LinkedList的源码如下(加入了比较详细的注释): package java.util; publi