Mongoose源码剖析:Introduction and Installation

本文详细介绍了Mongoose的使用方法,包括安装、编译、配置和嵌入到C/C++应用程序中。通过亲身体验,读者可以了解如何将Mongoose集成到现有项目中,提升开发效率。
引言

要剖析Mongoose的源码,首先你得知道它的一些基本情况和特性、并去使用它。本文就是介绍Mongoose是个什么东西?及如何安装和使用?这里假设你知道什么web服务器软件、web服务器使用什么协议、怎么提供服务等,如果你还不知道建议首先阅读:《Mongoose源码剖析:外篇之web服务器》。

本文主要内容如下:

  • 1、Mongoose介绍
  • 2、Mongoose的编译和使用
  • 3、Makefile的简单分析
  • 4、如何将Mongoose嵌入到应用程序中
1、Mongoose介绍

Mongoose开源项目的主页是http://code.google.com/p/mongoose/。Mongoose是一个web服务器,通过提供一个web接口给它,它可以嵌入到现有的应用程序中去。Mongoose web服务器的执行是自满足的,它不依赖于任何其他服务。如果你将它复制到任何目录并执行,它将将启动web服务并将当前目录作为主目录、端口号是8080。当然这些配置选项都可以通过配置文件mongoose.conf设置。

Mongoose它的前身是shttpd,它完全开源和自由使用。它还具有如下特性:

  • 跨平台——Windows、MacOS 、 大多数UNIX
  • CGI, SSL, SSI, Digest (MD5) authorization, resumed download, aliases
  • IP-based ACL, Windows service, GET, POST, HEAD, PUT, DELETE methods
  • Small footprint: executable size is 40 kB on Linux 2.6 i386 system, 60 kB on Windows system
  • 能够用简单和干净的API嵌入到应用程序中
  • Language suport for:
    • C/C++ (native)
    • Python - since version 2.6 (done)
    • C# - since version 2.7 (done)
    • Ruby - since version 2.9 (todo)
    • Lua - since version 2.9 (todo)
2、Mongoose的编译和使用

关于Mongoose上面应该已经介绍了它的主要特性。所谓耳听为虚,眼见为实,不过现在这个时代眼见也未必为实,正所谓实践见真知,下面还是来亲身体验下。Windows版本的Mongoose安装和使用,我就不介绍了,众所周知Windows下面的软件的安装基本都是傻瓜式的。下面我介绍的是linux下mongoose的编译和使用(我使用的linux是Ubuntu 10.04)。

首先去主页上面下载Mongoose的源码(mongoose-2.8.tgz )。将其解压可以看到如下的文件列表:

image_thumb1

图1、mongoose源码的文件列表

其实我们主要用到的就是mongoose.h/c文件,像main.c文件只是用来测试用的。如果你想将mongoose嵌入到你的项目中,主需要用到mongoose.h/c就ok了!而且以后主要的剖析工作就是放在mongoose.h/c上。其中的Makefile文件是用于编译,生成可执行文件和库文件等。

用make linux命令就可以将源码编译为linux版本的mongoose,如下图所示:

image_thumb4

图2、linux下面Mongoose的编译和执行

如上图,当我们输入make命令的时候,会提示你带相应环境的参数make (linux|bsd|solaris|mac|windows|mingw),这里我们选择linux。编译完之后,生成了两个文件:一个可执行文件mongoose、一个库文件_mongoose.so。你可以直接将_mongoose.so库文件拿到你的项目中去使用(当然是基于linux环境的项目,如果在Windows环境下是编译出相应的dll文件,去直接使用)。

从上图可以看出,编译完之后我们运行了(./mongoose),看到Mongoose 2.8 started on port(s) [8080], serving directory [/home/netsky/Downloads/mongoose]。即现在就启动了web server,当前工作目录是/home/netsky/Downsloads/mongoose,端口号是8080。我们可以在浏览器中输入http://localhost:8080/来访问web服务器(或者如果你跟我一样是在Ubuntu是运行在虚拟机中,你可以在外面的Windows系统中用浏览通过http://linux的ip:8080/来访问),如下图所示:

image_thumb6

图3、访问mongoose开启的web服务

我们查看mongoose打出来的log,如下图:

image_thumb10

图4、mongoose打印的log

从log可以看出有两个client,一个是我刚才在linux里面通过浏览器打开web服务的根目录;另一个是我在Windows下面通过浏览器打开web服务的根目录。

3、Makefile的简单分析

下面我们简单分析一下Makefile文件,看我们执行make linux的时候,编译了什么源文件,及编译的一些选项。

从PROG= mongoose和LIB= _$(PROG).so可以知道编译生成的执行文件名和库的名字;

从linux: 
$(CC) $(LINFLAGS) mongoose.c -shared -fPIC -fpic -s -o $(LIB) 
$(CC) $(LINFLAGS) mongoose.c main.c -s -o $(PROG) 
我们知道,在linux下面编译用到了哪些源文件:编译库文件_mongoose.so时仅用到了mongoose.c文件,编译可执行文件时除了mongoose.c文件还用到了main.c。注意上面的变量$(LINFLAGS),即编译时的选项为: 
image_thumb13

其中$(COPT)选项是我们make的时候跟的一些选项,下面列出这些选项及其意义:

all: 
        @echo "make (linux|bsd|solaris|mac|windows|mingw)"

# Possible COPT values: (in brackets are rough numbers for 'gcc -O2' on i386) 
# -DHAVE_MD5            - use system md5 library (-2kb) 
# -DNDEBUG              - strip off all debug code (-5kb) 
# -DDEBUG               - build debug version (very noisy) (+7kb) 
# -DNO_CGI              - disable CGI support (-5kb) 
# -DNO_SSL              - disable SSL functionality (-2kb) 
# -DCONFIG_FILE=\"file\" - use `file' as the default config file 
# -DNO_SSI              - disable SSI support (-4kb) 
# -DHAVE_STRTOUI64      - use system strtoui64() function for strtoull()

Makefile就简单的分析到这了,现在应该比较清楚可执行文件及库是怎么生成的了。(提示:Windows下面的编译,Makefile文件中也已经给出了,请查看Makefile文的后半部分。)

4、如何将Mongoose嵌入到应用程序中

目前Mongoose可以支持嵌入到C/C++、Python、C#,而且在下一个版本(2.9)将支持Ruby、Lua。但在这里我只介绍怎么样将Mongoose嵌入到C/C++中。其实将Mongoose嵌入到C/C++应用程序中很简单,因为Mongoose本来就是用C编写的,因此可以非常简单的将它嵌入到你的应用程序中。

  • 首先,拷贝mongoose.h/c到你的源码目录下
  • 然后,在你的应用程序中启动和配置mongoose。其实自带的main.c就是一个很好的例子,当我们编mongoose的可执行文件时就用到了它。你完全可以把main.c当做你的应用程序,完全一样,当然业务逻辑不同。(后面假设你的应用程序名为main.c,当然怎么命名随你喜欢!)
  • 最后,跟上面我们分析的Makefile中介绍到的如何在linux下编译Mongoose一样,你也是用下面的命令编译: 
    cc main.c mongoose.c –ldl -lpthread –o test 
    关于编译选项-pthread:Mongoose中在程序中用到了pthread.h头文件中的函数时需要加这个选项。在编译的时候加上此选项会去链接libpthread.a或libpthread.so文件。

关于如何编写类似main.c代码调用mongoose提供的接口,这个工作在本系列后面的文章中介绍。

至此,将Mongoose嵌入到你的C/C++应用程序中的工作已经完成,现在你执行./test即可以应用你的应用程序,它调用到了mongoose提供的一些接口。

通常上面的介绍,是不是觉得在应用程序嵌入Mongoose很简单呢!是的就是这么简单。


作者:吴秦
出处:http://www.cnblogs.com/skynet/
本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名吴秦(包含链接).

你遇到的错误日志: ``` 55d25b 1 mongoose.c:4353:accept_conn 1 accept failed, errno 24 ``` 频繁出现,其中 **errno 24** 对应的是 `EMFILE` —— “Too many open files”,即 **进程打开的文件描述符(file descriptor)数量超过了系统限制**。 在类 Unix 系统中,每个 TCP 连接都会占用一个文件描述符。Mongoose 是一个轻量级的嵌入式网络库,使用 `accept()` 接受新连接时需要分配新的 socket 文件描述符。当系统或进程达到最大文件描述符限制时,`accept()` 就会失败并返回 `errno = 24`。 --- ### ✅ 根本原因分析 - **错误位置**:`mongoose.c:4353:accept_conn` 表明是在调用 `accept()` 函数时出错。 - **错误码 `errno 24`**:`EMFILE`,表示当前进程已经打开了太多文件(包括 socket),无法再打开新的 socket。 - 可能的原因: 1. 并发连接数过高,未及时关闭旧连接。 2. 存在连接泄漏(connection leak)—— 某些连接被建立但没有正确释放。 3. 进程级别的文件描述符限制太低。 4. 系统级的文件描述符总量不足。 --- ### ✅ 解决方案 #### 方案 1:增加进程文件描述符限制(ulimit) 查看当前限制: ```bash ulimit -n ``` 临时提高限制(例如设为 65536): ```bash ulimit -n 65536 ``` 然后重启你的程序。 > ⚠️ 注意:这只能临时生效。要永久生效需修改 `/etc/security/limits.conf`。 #### 方案 2:修改系统 limits(永久有效) 编辑: ```bash sudo vim /etc/security/limits.conf ``` 添加以下内容(假设运行程序的用户是 `youruser`): ``` youruser soft nofile 65536 youruser hard nofile 65536 ``` 如果是 systemd 启动的服务,还需要修改 systemd 配置: ```bash sudo systemctl edit your-service-name ``` 添加: ```ini [Service] LimitNOFILE=65536 ``` 然后重启服务。 #### 方案 3:检查代码中的资源泄漏 确保所有客户端连接都被正确关闭。比如,在处理完 HTTP 请求后,应该让 Mongoose 自动关闭连接,或者显式调用 `mg_close_conn()`。 示例:避免忘记关闭连接 ```c static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { switch (ev) { case MG_EV_HTTP_REQUEST: { struct http_message *hm = (struct http_message *) ev_data; // 处理请求... mg_printf(nc, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\nHello", 5); // Mongoose 默认会在发送完成后关闭连接(如果是 HTTP/1.0 或有 Connection: close) // 如果是 keep-alive,可能需要手动控制 // 若不想保持连接,可以主动关闭: // mg_send_head(nc, 200, 5, "Connection: close"); // mg_printf(nc, "Hello"); // nc->flags |= MG_F_SEND_AND_CLOSE; break; } } } ``` 关键点:对于不需要长连接的情况,设置 `MG_F_SEND_AND_CLOSE` 标志来及时关闭连接。 ```c nc->flags |= MG_F_SEND_AND_CLOSE; ``` #### 方案 4:启用连接超时和空闲回收 Mongoose 支持设置连接超时时间,防止连接长时间挂起。 ```c // 设置空闲超时(秒) mg_set_protocol_http_websocket(nc); nc->recv_mbuf_limit = 1024 * 1024; // 可选:限制接收缓冲区大小 ``` 也可以通过定时器清理长期无活动的连接。 #### 方案 5:监控 fd 使用情况(调试用) 你可以添加调试代码,打印当前进程打开的 fd 数量: ```c #include <dirent.h> #include <stdio.h> int count_open_fds() { DIR *dir = opendir("/proc/self/fd"); struct dirent *entry; int count = 0; while ((entry = readdir(dir)) != NULL) { if (entry->d_name[0] != '.') count++; } closedir(dir); return count; } // 在事件循环中定期打印 printf("Open FDs: %d\n", count_open_fds()); ``` --- ### ✅ 总结建议 | 措施 | 建议 | |------|------| | 提高 `ulimit -n` | 必做 | | 修改 `limits.conf` 和 systemd 配置 | 生产环境必做 | | 检查连接是否及时关闭 | 关键!排查泄漏 | | 使用 `MG_F_SEND_AND_CLOSE` | 控制连接生命周期 | | 添加 FD 监控 | 调试利器 | --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值