Mongoose 网络库之架构概述
Mongoose 是一个单文件、事件驱动的嵌入式 Web 服务器库——但这一简单的描述背后隐藏着精心设计的分层架构使其能够运行在从仅有 64KB 内存的裸机微控制器到完整的 Linux 服务器等各种平台上。本页将梳理该库的结构骨架其分层设计、核心数据结构以及使其能够适配不同目标平台的编译时配置系统。宏观概览分层栈Mongoose 的架构遵循严格的分层原则每一层仅依赖于其下层。公开的 API 接口——即单一的mongoose.h头文件和合并后的mongoose.c源文件——位于最顶层而与硬件相关的细节则被限制在最底层。这种设计使得该库能够跨 16 种以上的架构进行移植而无需在应用代码中到处充斥着条件编译指令。每个连接都流经此协议栈I/O 层检测套接字的就绪状态事件管理器将事件分发给协议处理器协议处理器再调用用户的回调函数。这种控制反转——由库来调用你而不是你主动调用库——是塑造其他所有内容的基础架构决策。核心数据结构Mongoose 的架构建立在两个基础数据结构之上它们共同构成了整个运行时状态连接管理器和连接本身。连接管理器 (struct mg_mgr)mg_mgr结构体是拥有所有运行时状态的顶层对象。每个 Mongoose 应用程序仅创建一个管理器在多线程设计中则为每个线程创建一个。它持有活动连接的链表、DNS 解析器状态、共享的 TLS 上下文、定时器链以及指向可选内置 TCP/IP 网络接口的指针。连接 (struct mg_connection)每个网络连接——无论是监听套接字、已接受的客户端还是外发连接——都由从管理器内存池中分配的mg_connection来表示。连接构成了一个以mg_mgr.conns为锚点的单向链表。该结构体不仅携带底层 I/O 状态文件描述符、本地/远程地址、发送/接收 IO 缓冲区还通过双重处理器模式携带高层协议状态。双重处理器模式每个连接都携带两个事件处理函数指针pfn协议处理器和fn用户处理器。当事件触发时协议处理器先执行随后执行用户处理器。这种分离使得 Mongoose 内部的协议实现HTTP、WebSocket、MQTT能够在用户应用程序代码接收到数据之前先解析传入的数据并发出更高层级的事件。事件驱动运行时Mongoose 的核心是mg_mgr_poll()函数——一个驱动所有网络活动的单一同步循环。每次调用mg_mgr_poll()都会按顺序执行以下三个阶段I/O 多路复用—— 使用当前系统可用的最佳系统调用在 POSIX 上为 epoll poll select在 FreeRTOS-TCP 上为 FreeRTOS_select测试所有活动套接字的可读/可写状态。定时器过期—— 遍历定时器链表并触发已过期的回调。连接处理—— 遍历所有连接分发MG_EV_POLL事件然后根据连接标志和 I/O 就绪状态执行状态机转换accept、connect、read、write、TLS 握手。这种设计意味着整个库都是非阻塞的没有任何单个连接能够饿死其他连接并且 poll 的超时参数可直接控制最小的空闲延迟。事件枚举Mongoose 定义了一组精简的事件涵盖了完整的连接生命周期和特定协议的通知类别事件用途连接生命周期MG_EV_OPEN、MG_EV_ACCEPT、MG_EV_CONNECT、MG_EV_CLOSE、MG_EV_POLL跟踪连接状态转换数据传输MG_EV_READ、MG_EV_WRITE带有字节数的原始 I/O 事件错误MG_EV_ERROR携带人类可读的错误字符串DNSMG_EV_RESOLVE主机名解析完成TLSMG_EV_TLS_HSTLS 握手成功HTTPMG_EV_HTTP_HDRS、MG_EV_HTTP_MSG已解析的 HTTP 头部和完整消息WebSocketMG_EV_WS_OPEN、MG_EV_WS_MSG、MG_EV_WS_CTLWebSocket 生命周期及数据MQTTMG_EV_MQTT_CMD、MG_EV_MQTT_MSG、MG_EV_MQTT_OPENMQTT 协议事件工具MG_EV_WAKEUP、MG_EV_SNTP_TIME、MG_EV_USER线程间唤醒、时间同步、用户自定义I/O 缓冲区管理数据通过两个mg_iobuf结构体流经每个连接——recv用于接收传入数据send用于发送传出数据。IO 缓冲区是一个包含四个字段的最小化动态缓冲区数据指针、已分配容量、当前长度和对齐约束。缓冲区以MG_IO_SIZE默认为 512 字节为步长自动增长并受MG_MAX_RECV_SIZE默认 3 MB上限限制以防止无限制读取导致内存耗尽。网络 I/O 策略Mongoose 支持四种不同的网络后端通过编译时配置标志进行选择。在任何给定的构建版本中仅有一个套接字后端处于活动状态后端配置标志I/O 机制目标环境POSIX 套接字 (select)MG_ENABLE_SOCKET1(默认)select()Linux、macOS、BSD、WindowsPOSIX 套接字 (poll)MG_ENABLE_POLL1poll()Linux、macOS比 select 扩展性更好POSIX 套接字 (epoll)MG_ENABLE_EPOLL1epoll_wait()Linux最适合高连接数场景FreeRTOS-TCPMG_ENABLE_FREERTOS_TCP1FreeRTOS_select()Amazon FreeRTOS 设备lwIPMG_ENABLE_LWIP1lwIP 套接字 APIESP-IDF、Zephyr (lwIP 模式)ARM MDK (RL)MG_ENABLE_RL1ARM RL-TCPnet APIKeil MDK 工程内置 TCP/IPMG_ENABLE_TCPIP1自定义裸机协议栈无操作系统网络栈的裸机 MCUsock.c中的 I/O 测试函数mg_iotest()在预处理器指令的保护下实现了所有四种策略无论激活的是哪种后端都向 poll 循环提供统一的is_readable/is_writable接口。内置 TCP/IP 协议栈当设置MG_ENABLE_TCPIP1时Mongoose 会用其自身的裸机 TCP/IP 实现替换 POSIX 套接字层。该协议栈提供完整的 IPv4可选 IPv6网络协议栈包括 ARP、DHCP 客户端/服务端、ICMP、UDP 和 TCP——所有这些仅需约 20KB 的代码。该架构以struct mg_tcpip_if为核心它表示单一网络接口并持有 IP 配置、驱动函数指针和协议状态。驱动接口通过struct mg_tcpip_driver进行抽象——包含用于init、tx发送帧、rx接收帧和poll链路状态的三个函数指针。src/drivers/目录包含针对 20 多种 MCU 外设的实现包括 STM32 Ethernet MAC、Wiznet W5500、PIC32、IMXRT 和 RP2040 Pico-W WiFi。协议实现协议模块位于核心运行时之上利用双重处理器机制拦截原始的MG_EV_READ事件解析协议帧并重新发出更高层级的事件。每个协议模块都遵循相同的架构契约HTTP(src/http.h、src/http.c) —— 解析 HTTP 请求/响应消息提供mg_http_listen()、mg_http_connect()、mg_http_serve_dir()及文件服务工具。WebSocket(src/ws.h、src/ws.c) —— 升级 HTTP 连接处理帧编码/解码发出MG_EV_WS_MSG和MG_EV_WS_CTL事件。MQTT(src/mqtt.h、src/mqtt.c) —— 完整的 MQTT v5 客户端支持发布/订阅、属性支持及自动保活。TLS 架构TLS 支持通过策略模式实现可插拔。mg_tls_init()函数根据编译时常量MG_TLS分发至所选后端该常量可设置为 mbedTLS、OpenSSL、WolfSSL、内置实现或自定义提供者。TLS 层封装底层 I/O 通道并通过连接的rtls原始 TLS缓冲区呈现加密数据随后将其解密到标准的recv缓冲区中。平台抽象层src/arch.h文件是所有平台特定适配的唯一入口点。它根据MG_ARCH常量选择合适的架构头文件并引入 lwIP、ARM RL 和 FreeRTOS-TCP 的网络协议栈头文件。每个arch_*.h文件精确定义了核心库所需的内容内存分配宏mg_malloc、mg_free、时间函数mg_millis、线程原语、套接字类型和调试输出。当前支持的架构横跨 16 个目标平台从 Unix 和 Windows 到 ESP32、STM32Cube、Zephyr、ThreadX 以及裸机 ARM GCC。辅助子系统几个实用模块完善了整体架构每个模块都具有简洁的接口和最少的依赖模块文件职责DNSdns.c、dns.h基于 UDP 的非阻塞 DNS 解析SNTPsntp.c、sntp.h简单网络时间协议客户端定时器timer.c、timer.h单次和周期性定时器链文件系统fs.c、fs.h、fs_posix.c、fs_packed.c、fs_fat.c可插拔 VFSPOSIX、打包闪存、FATJSONjson.c、json.h极简 JSON 解析器/生成器OTAota_*.c、ota.h支持 8 个以上 MCU 系列的固件更新字符串/格式化str.c、str.h、fmt.c、fmt.h内存安全的字符串操作与 printf编译时配置Mongoose 完全通过预处理器宏定义进行配置——没有运行时配置文件也没有链接器脚本。src/config.h文件为每个选项提供了合理的默认值特定架构的头文件会根据需要覆盖这些默认值。主要的配置类别包括功能开关—— 启用/禁用协议TLS、HTTP、MQTT、WebSocket和子系统DNS、SNTP、SSI、FAT 文件系统。内存调优——MG_IO_SIZE、MG_MAX_RECV_SIZE、MG_DATA_SIZE用于控制缓冲区粒度和每个连接的开销。网络后端选择——MG_ENABLE_TCPIP、MG_ENABLE_LWIP、MG_ENABLE_SOCKET、MG_ENABLE_EPOLL。内置 TCP/IP 默认值——MG_TCPIP_IP、MG_TCPIP_MASK、MG_TCPIP_GW用于静态 IP 配置。所有默认值都可以通过在包含mongoose.h之前定义它们来覆盖通过使用mongoose_config.h文件需设置MG_ARCHMG_ARCH_CUSTOM或者通过构建系统标志进行覆盖。源码组织该库以两种形式提供位于src/下的独立源文件用于细粒度构建以及位于仓库根目录的单一合并文件mongoose.c/mongoose.h以实现最大的集成简便性。src/目录按职责而非层级来组织模块src/ ├── 核心运行时 → event.c, net.c, sock.c, timer.c, queue.c, iobuf.c ├── 协议 → http.c, ws.c, mqtt.c ├── TLS 后端 → tls_builtin.c, tls_mbed.c, tls_openssl.c, tls_*.c ├── 网络协议栈 → net_builtin.c, l2.c, l2_eth.c, l2_ppp.c ├── 驱动 → drivers/stm32f.c, drivers/w5500.c, drivers/pico-w.c, ... ├── 架构 → arch_unix.h, arch_esp32.h, arch_win32.h, arch_*.h ├── 工具 → str.c, fmt.c, json.c, base64.c, md5.c, sha1.c, sha256.c ├── 文件系统 → fs.c, fs_posix.c, fs_packed.c, fs_fat.c ├── OTA 更新 → ota_stm32f.c, ota_esp32.c, ota_imxrt.c, ... └── 配置 → config.h, arch.hmg_connection.data字段为每个连接提供了一个固定大小默认 32 字节的暂存区在向事件处理器传递少量状态时无需进行单独的内存分配。如果你的处理器需要更多的每连接存储空间可以在编译时增加MG_DATA_SIZE—— 在内存受限的设备上这比使用外部哈希映射更高效。在针对裸机 MCU 时设置MG_ENABLE_TCPIP1并从src/drivers/中选择一个驱动——这消除了对任何外部网络协议栈的需求并将依赖面缩减为仅 MCU 的以太网/WiFi 外设 HAL。内置协议栈完全在mg_mgr_poll()调用内处理 DHCP、ARP 和 TCP 重传无需后台线程或中断驱动的网络。

相关新闻