Mongoose 是一个单文件、事件驱动的 C/C++ 网络库,它将 TCP、UDP、HTTP、WebSocket、MQTT 和 TLS 协议引入了几乎任何平台——从裸机微控制器到云服务器。自 2004 年发布以来,它已部署到全球数亿台设备(包括国际空间站),其存在的意义在于让嵌入式网络编程变得快速、健壮且异常简单。
什么是 Mongoose?
在其核心,Mongoose 提供了一个由中央事件循环驱动的异步非阻塞 I/O 模型。你创建一个事件管理器,为所需的协议注册监听器,提供一个回调函数,然后让mg_mgr_poll()循环处理其余的一切——连接生命周期、数据分帧、协议解析、TLS 握手、DNS 解析和超时处理。这里没有线程要求,没有外部依赖,也没有需要费力应对的复杂构建系统。
整个库仅以两个文件发布——mongoose.c和mongoose.h——你可以将它们放入任何项目中,并通过单条命令进行编译。尽管如此极简,其源码完全符合 ISO C 和 ISO C++ 标准,当前版本为7.21。
它的独特之处
传统的嵌入式网络方法往往迫使人面临艰难的权衡:要么采用消耗宝贵 Flash 和 RAM 的重量级 TCP/IP 协议栈加操作系统组合,要么从头开始构建一切,并独自承担协议正确性和安全的全部重担。Mongoose 占据了一个独特的中间地带。它可以运行在任何兼容 BSD socket 的协议栈(lwIP、Zephyr、Azure RTOS 等)之上,或者直接在裸机硬件上运行其自带的 TCP/IP 协议栈——完全消除了对任何第三方网络软件的需求。
高层架构
每个 Mongoose 应用程序都围绕相同的基本模式构建:一个事件管理器持有一个由连接组成的链表,每个连接携带一个用户提供的事件处理函数,而一个轮询循环驱动这一切向前运行。特定协议的逻辑(HTTP 解析、WebSocket 分帧、MQTT 消息处理)分层构建在这个统一的抽象之上,将原始的 socket 字节转化为你的处理函数接收的结构化事件。
这种架构意味着你只需学习一种 API 模式,就能将其应用于所有协议。无论你是在提供 Web 仪表板、发布 MQTT 遥测数据,还是建立用于实时控制的 WebSocket,其基础代码结构都是完全相同的。
支持的协议
Mongoose 实现了现代物联网和嵌入式设备所需的完整网络协议套件。每个协议都通过遵循相同事件处理模式的整洁、一致的 API 接口暴露出来。
| 协议 | 角色 | 核心头文件 | 描述 |
|---|---|---|---|
| TCP / UDP | 传输层 | src/net.h | 原始连接管理、发送/接收、DNS 解析 |
| HTTP | 应用层 | src/http.h | 完整的 HTTP/1.1 服务端和客户端、REST API、文件服务、分块上传 |
| WebSocket | 应用层 | src/ws.h | 文本和二进制帧、ping/pong、从 HTTP 升级 |
| MQTT | 应用层 | src/mqtt.h | 支持 QoS 的发布/订阅客户端,已为 MQTT v5 做好准备 |
| TLS | 安全层 | src/tls.h | 内置支持 ECC 的 TLS 1.3,或可插拔的 mbedTLS/OpenSSL 后端 |
| DNS | 基础设施 | src/dns.h | 异步非阻塞 DNS 解析器(A、AAAA、SRV 记录) |
| SNTP | 基础设施 | src/sntp.h | 通过 NTP 服务器进行网络时间同步 |
| JSON-RPC | 应用层 | src/rpc.h | 在任何传输层上运行的内置远程过程调用框架 |
| OTA | 基础设施 | src/ota.h | 支持 15 个以上 MCU 系列的空中固件升级 |
平台覆盖
Mongoose 最引人注目的属性在于其广泛的平台支持。相同的源代码可以在工作站、嵌入式 Linux 系统、RTOS 环境以及完全裸机的微控制器上编译和运行。这是通过一个平台抽象层(src/arch_*.h)实现的,该层将所有特定于操作系统和硬件的细节隔离在一组整洁的宏和函数签名之后 。
外部 TCP/IP 协议栈模式
在现有的网络协议栈上运行时,Mongoose 使用标准的 BSD socket API。这几乎涵盖了所有已具备 TCP/IP 协议栈的平台。
| 平台 | TCP/IP 协议栈 | 用例 |
|---|---|---|
| Linux、macOS、Windows | 原生 OS sockets | 开发、测试、服务端 |
| ESP32 / ESP32-S3 / ESP32-C3 | lwIP | Espressif Wi-Fi MCU |
| Zephyr RTOS | Zephyr 原生 | Nordic、STM32 及其他 Zephyr 开发板 |
| FreeRTOS | FreeRTOS-TCP、lwIP | 通用 RTOS 环境 |
| 任何 BSD socket 协议栈 | 自定义 | Amazon FreeRTOS-TCP 等 |
内置 TCP/IP 协议栈模式
Mongoose 自带了其 TCP/IP 协议栈(MG_ENABLE_TCPIP),允许它直接在具备以太网能力的 MCU 上运行,且零外部网络依赖。这在资源受限的设备上特别有价值,因为引入 lwIP 可能显得大材小用。在裸机 STM32 Nucleo 开发板上实现一个完整的 Web 设备仪表板仅需 6 个文件——相比之下,典型的 CubeIDE 生成的 HTTP 示例则需要 400 多个文件。
| 硬件系列 | 支持的芯片 | 驱动位置 |
|---|---|---|
| STM32 | Fxx、H5xx、H7xx | src/drivers/stm32f.c、stm32h.c |
| NXP | i.MX RT10xx、RT11xx、RW612、MCXN94x | src/drivers/imxrt.c、rw612.c |
| Microchip | ATSAME54 | src/drivers/same54.c |
| Renesas | RA5M、RA6M、RA8M | src/drivers/ra.c |
| Infineon | XMC4、XMC7 | src/drivers/xmc.c、xmc7.c |
| Texas Instruments | TM4C、TMS570 | src/drivers/tm4c.c、tms570.c |
| Wiznet | W5500、W5100 | src/drivers/w5500.c、w5100.c |
| Cypress WiFi | CYW43xx (Pico-W、Portenta) | src/drivers/pico-w.c |
事件系统
理解事件系统是高效使用 Mongoose 最重要的一步。每一个连接——无论是监听套接字、已接受的客户端连接、外发的 MQTT 链接,还是 WebSocket——都会向你提供的一个回调函数触发事件。ev参数告诉你发生了什么,而ev_data携带了特定协议的上下文。
核心连接事件
| 事件 | 含义 | ev_data类型 |
|---|---|---|
MG_EV_OPEN | 连接已创建 | NULL |
MG_EV_CONNECT | 出站连接已建立 | NULL |
MG_EV_ACCEPT | 入站连接已接受 | NULL |
MG_EV_READ | 从套接字接收到数据 | long *(读取的字节数) |
MG_EV_WRITE | 数据已写入套接字 | long *(写入的字节数) |
MG_EV_CLOSE | 连接已关闭 | NULL |
MG_EV_ERROR | 发生错误 | char *(错误消息) |
特定协议事件
| 事件 | 协议 | 含义 |
|---|---|---|
MG_EV_HTTP_MSG | HTTP | 已解析完整的 HTTP 请求/响应 |
MG_EV_HTTP_HDRS | HTTP | 已接收 HTTP 头部(正文之前) |
MG_EV_WS_OPEN | WebSocket | 握手完成,升级成功 |
MG_EV_WS_MSG | WebSocket | 接收到文本或二进制消息 |
MG_EV_WS_CTL | WebSocket | 控制帧(关闭、ping、pong) |
MG_EV_MQTT_OPEN | MQTT | 收到 CONNACK,连接就绪 |
MG_EV_MQTT_MSG | MQTT | 收到 PUBLISH 消息 |
MG_EV_SNTP_TIME | SNTP | 已从 NTP 服务器同步时间 |
MG_EV_TLS_HS | TLS | TLS 握手已完成 |
项目结构
代码仓库被组织为三个顶层区域:根目录下的单文件发行版、src/下的模块化源码树,以及丰富的教程和测试集合。
mongoose/ ├── mongoose.c ← 单文件合并版(所有代码编译在一起) ├── mongoose.h ← 公共 API 头文件(版本、架构定义、所有声明) │ ├── src/ ← 用于高级集成的模块化源码 │ ├── arch_*.h ← 平台抽象:UNIX、WIN32、ESP32、Zephyr 等 │ ├── config.h ← 编译时功能标志 (MG_ENABLE_*) │ ├── event.h / event.c ← 事件分发系统 │ ├── net.h / net.c ← 连接管理器 (mg_mgr, mg_connection) │ ├── net_builtin.c/h ← 内置 TCP/IP 协议栈 │ ├── drivers/ ← 内置 TCP/IP 的硬件驱动(30 多个文件) │ ├── http.h / http.c ← HTTP 服务端和客户端 │ ├── ws.h / ws.c ← WebSocket 协议 │ ├── mqtt.h / mqtt.c ← MQTT 客户端 │ ├── tls_*.c / tls_*.h ← TLS 后端(内置、mbedTLS、OpenSSL) │ ├── dns.h / dns.c ← 异步 DNS 解析器 │ ├── sntp.h / sntp.c ← SNTP 时间客户端 │ ├── fs.h / fs_*.c ← 文件系统抽象(POSIX、FAT、打包文件) │ ├── ota_*.c / ota.h ← OTA 固件升级(15 个以上 MCU 系列) │ ├── rpc.h / rpc.c ← JSON-RPC 框架 │ ├── json.h / json.c ← JSON 解析器 │ ├── iobuf.h / iobuf.c ← IO 缓冲区管理 │ ├── timer.h / timer.c ← 定时器子系统 │ └── ... ← base64、md5、sha1、sha256、fmt、str、queue、log │ ├── tutorials/ ← 30 多个协议和平台教程 │ ├── core/ ← 基本概念(事件循环、嵌入式文件系统) │ ├── http/ ← HTTP 服务端、REST API、文件上传、认证 │ ├── websocket/ ← WebSocket 回显、聊天、仪表板 │ ├── mqtt/ ← MQTT 发布/订阅 │ ├── tls/ ← TLS 服务端和客户端 │ ├── tcp/ / udp/ ← 原始 TCP/UDP 通信 │ ├── stm32/ / esp32/ / ← 按厂商划分的硬件特定教程 │ │ nxp/ / zephyr/ / │ │ pico-sdk/ / ... │ └── device-dashboard/ ← 完整的 Web UI 仪表板示例 │ └── test/ ← 单元测试、模糊测试、硬件测试套件构建配置
Mongoose 完全通过在src/config.h中定义的编译时宏进行控制。每个功能都可以独立启用或禁用,从而允许你仅编译项目所需的内容,并将 Flash/RAM 占用降至最低。
极小体积:在禁用所有协议的情况下,Mongoose 编译后的 Flash 占用约为 30KB。一个带有 TLS 的典型 HTTP 服务端根据所选的 TLS 后端,大约会增加 100–200KB。MG_IO_SIZE宏(默认为 512 字节)控制 IO 缓冲区的粒度——在 RAM 受限的设备上可以减小它,以牺牲吞吐量为代价来换取内存。
| 宏 | 默认值 | 用途 |
|---|---|---|
MG_ENABLE_TCPIP | 0 | 启用内置 TCP/IP 协议栈(裸机) |
MG_ENABLE_LWIP | 0 | 使用 lwIP 作为网络后端 |
MG_TLS | MG_TLS_NONE | TLS 后端:BUILTIN、MBED、OPENSSL、WOLFSSL |
MG_OTA | MG_OTA_NONE | OTA 支持:STM32H5、STM32H7、ESP32、PICOSDK等 |
MG_ENABLE_SOCKET | !MG_ENABLE_TCPIP | BSD socket 支持(使用内置协议栈时自动禁用) |
MG_ENABLE_FATFS | 0 | FAT 文件系统支持 |
MG_ENABLE_IPV6 | 0 | IPv6 协议支持 |
MG_ENABLE_DIRLIST | 0 | HTTP 目录列表 |
MG_IO_SIZE | 512 | IO 缓冲区增长粒度(字节) |
MG_MAX_RECV_SIZE | 3MB | 最大接收缓冲区大小 |
MG_ENABLE_PROFILE | 0 | 用于性能分析的连接性能剖析 |
安全态势
Mongoose 持续受到多层自动化和手动安全分析的审查。代码仓库集成了GitHub Actions CI(在每次提交时使用地址 sanitizer 运行数百个单元测试)、Google 的 OSS-Fuzz持续模糊测试工具,并定期接收来自独立安全组织(包括 Cisco Talos、Microsoft Security Response Center、MITRE Corporation 和 Compass Security)的漏洞报告。
双重许可:Mongoose 针对开源项目以 GPLv2 发布,同时为专有产品提供商业许可证。商业许可证包含优先的安全通知——在规模化部署生产设备时,这一点至关重要 。
API 初探
理解 Mongoose 最好的方式就是看它的实际运行。下面这个完整的 HTTP 服务端——用于提供目录中的文件——仅用不到 15 行 C 代码即可实现:
static void timer_fn(void *arg) {
struct mg_mgr *mgr = (struct mg_mgr *) arg;
for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) {
if (c->data[0] == 'W') mg_ws_send(c, "hi", 2, WEBSOCKET_OP_TEXT);
}
}
int main(void) {
struct mg_mgr mgr;
mg_mgr_init(&mgr);
mg_timer_add(&mgr, 1000, MG_TIMER_REPEAT, timer_fn, &mgr); // 每 1 秒
mg_http_listen(&mgr, "http://localhost:8000", fn, NULL);
for (;;) mg_mgr_poll(&mgr, 500);
mg_mgr_free(&mgr);
return 0;
}
编译命令:cc main.c mongoose.c -I.——这就是整个构建系统。