物联网网关以太网协议栈怎么实现:lwIP从ARP解析到TCP滑动窗口七步讲透

MCU 跑以太网上云,最忌自己从以太网帧往上造轮子。本文基于《TCP/IP技术大全》等资料,讲清 TCP/IP 分层模型与 lwIP 模块映射、ARP 解析与 IP 分片、UDP 与 TCP 选型、三次握手、滑动窗口与慢启动、pbuf 三种内存策略,附 STM32+DM9161 初始化与 MQTT 上云 socket 代码,物联网网关以太网七步落地。

2026-10-14
TCP/IPlwIP物联网网关以太网滑动窗口MQTT

给物联网网加一路 100M 以太网上云,硬件上 DM9161 一类 PHY 芯片只解决"电信号↔MAC 帧"这一层,从 MAC 帧到 MQTT 报文之间还隔着整条协议栈——这条栈在 PC 上是操作系统自带的,在 MCU 上必须自己解决。结论先给三条:第一,嵌入式以太网协议栈不要自研,lwIP 是行业事实标准,几十 KB 级 Flash、10KB 级 RAM 起步可裁剪,支持无 OS 与带 OS 两种运行模式;第二,协议栈的坑八成在内存管理和 TCP 参数上——pbuf 分配策略选错会碎片化,MSS/滑动窗口/拥塞窗口不调,吞吐直接腰斩;第三,UDP 与 TCP 按业务选,Modbus TCP、MQTT 走 TCP,SNTP、高频遥测心跳走 UDP,别一刀切。下面按分层模型、ARP/IP、传输层选型、TCP 可靠性机制、内存策略、代码与落地七步讲透。

一、为什么 MCU 上以太网必须先懂分层

先看问题本身。以太网物理层给你的是一帧一帧的 MAC 帧:目的 MAC、源 MAC、类型、载荷、CRC。应用层要的却是一个能 connect() 到 MQTT 服务器 1883 端口的"管道"。这两者之间隔着寻址、路由、可靠传输、拥塞控制、字节流切分等一整套机制——《TCP/IP技术大全》把这套机制归结为开放式通信模型的分层处理思想:每层只对上层提供服务、只依赖下层的服务,层间接口固定,任一层的技术演进不影响邻层(第 1 章"开放式通信模型简介")。OSI 参考模型分了七层,TCP/IP 实用模型收敛为四层,嵌入式协议栈就是把这个四层模型装进几百 KB 以内的 MCU 里。

lwIP(Lightweight IP)是瑞典计算机科学研究所(SICS)开源的轻量级 TCP/IP 协议栈,BSD 风格许可,是 STM32、NXP、乐鑫等主控以太网方案的默认选择。它的两个关键开关:NO_SYS 为 1 时以无 OS 轮询方式运行,适合裸机;为 0 时以 TCP/IP 线程方式运行,socket/信号量/邮箱全套可用,适合带 RTOS 的网关。

RS-485 子设备 Modbus 传感器 STM32 MCU 应用: MQTT 客户端 lwIP 协议栈 + ETH MAC DM9161 PHY MII/RMII 10/100M RJ45 云平台 Broker MQTT :1883 / 8883 数据库 / 规则引擎 TCP 三次握手 → CONNECT 报文 → SUBSCRIBE/PUBLISH → PING keepalive
图 1 物联网网关以太网上云通路:lwIP 位于应用与 ETH MAC 之间

二、四层模型与 lwIP 模块映射:一张表定架构

读懂协议栈先定架构。把 OSI 七层、TCP/IP 四层与 lwIP 实际源码模块对上号,每个问题就知道了该去改哪个文件:

OSI 七层TCP/IP 四层lwIP 模块 / 文件网关中解决的问题
应用层 / 表示层 / 会话层应用层apps/mqtt、apps/httpd、netdbMQTT 收发、远程配置网页
传输层传输层tcp.c / udp.c / raw.cTCP 连接维护、UDP 遥测
网络层网际层ip4.c、icmp.c、etharp.cIP 寻址、ping、ARP 解析
数据链路层 / 物理层网络接口层netif.c + 网卡驱动(STM32 HAL ETH)MAC 帧收发、DMA 描述符
(物理层独立于协议栈)DM9161 等 PHY 芯片10/100M 自适应、MDI/MDIX

分层铁律是:跨层直接调用的代码今天省十行,明天协议一变就全崩。应用只准走 socket API(或 lwIP 的 netconn/raw 回调 API),MAC 地址、IP 分片这些事交给下面几层。

三、ARP 与 IP:一帧数据上链路前发生了什么

应用调 connect("192.168.1.100", 1883) 后,协议栈第一件事不是发 TCP SYN,而是查 ARP 缓存(RFC 826):目标 IP 有没有对应 MAC?没有就先广播一条 ARP 请求"谁是 192.168.1.100",拿到应答后缓存表项(典型老化时间几分钟到 20 分钟),再发业务报文。IP 层(RFC 791)随后做三件事:填 20 字节基本报头(无选项时)、按目的地址决定直接交付还是走默认网关、载荷超过 MTU(以太网 1500 字节)时分片——嵌入式端最好通过 MSS 协商让 TCP 报文天然小于 MTU,避免 IP 分片,一片丢全组重传。

工程要点:ICMP(RFC 792)的 ping 一定要开——现场调试 80% 的"连不上云"先用 ping 判定是二层通还是三层通;网关产品建议同时支持 DHCP 与静态 IP 双模式,掉线重连时先免费 ARP 宣告地址,避免与现场设备 IP 冲突后静默丢包。

四、UDP 还是 TCP:按报文价值选,不按习惯选

传输层两条路差别极大,选错方向整个网关的时延与可靠性模型都会错位。核心对比:

对比项UDPTCP
报头开销8 字节20 字节(无选项)
连接与状态无连接,无状态三次握手建链,11 种状态机
可靠性不保证送达、不保证顺序确认重传、按序交付、校验和
拥塞控制无慢启动 + 拥塞避免 + 快重传
典型时延一跳即发,毫秒级建链 1.5×RTT 后才可发数据
典型应用端口SNTP :123、高频遥测心跳Modbus TCP :502、MQTT :1883/:8883、HTTP :80

网关选型经验:指令与账目走 TCP,毛刺与心跳走 UDP。Modbus TCP 一条写寄存器指令不能丢也不能重,必须 TCP;MQTT 协议本身指定承载在 TCP 上,QoS 机制建立在可靠字节流之上;而每秒几十点的高频采样如果走 TCP,一次丢包重传会把整条流卡住,拆成带序号的 UDP 小报文、应用层容丢,反而更平滑。

五、TCP 可靠性机制:握手、滑动窗口与慢启动

TCP(RFC 793)的可靠不是玄学,是三个机制的组合:

  1. 三次握手:客户端 SYN(SEQ=x) → 服务端 SYN+ACK(SEQ=y, ACK=x+1) → 客户端 ACK(ACK=y+1)。同步双方初始序号,防止旧连接的报文串台;断开则是对称的四次挥手,TIME_WAIT 状态等 2×MSL 后释放。
  2. 滑动窗口:接收方在每个 ACK 里通告剩余缓冲 rwnd,发送方未确认字节不得超过窗口——这是"对端能收多少"的流量控制。发送侧还有拥塞窗口 cwnd:慢启动阶段从 1×MSS 起每 RTT 翻倍,达到阈值 ssthresh 后进入拥塞避免,每 RTT 线性加 1×MSS;丢包后减窗重传。两个窗口取小值决定实际发送速度。
  3. 超时重传:RTO 基于往返时延的平滑估计动态计算,网络抖动越大 RTO 越大,避免风暴式重传。

对以太网 MTU 1500 字节、IP 头 20、TCP 头 20,MSS 满配就是 1460 字节。lwIP 里 tcp_mss、tcp_wnd、tcp_sndbuf 三个参数直接决定吞吐:经验值 tcp_wnd ≥ 4×MSS、tcp_sndbuf ≥ 2×MSS,RAM 富余时窗口翻倍往往比换主频更有效。10M/100M 带宽下,百字节级小包每包都带完整 20+20 字节头部,应用有效载荷率不足 70%——批量遥测在应用层攒包再发,性价比远高于堆硬件。

六、内存管理:pbuf 三种策略决定稳定还是崩溃

lwIP 长期跑不跑得稳,八成看内存。它用 pbuf(packet buffer)链描述报文,三种来源策略各有适用:

策略机制优点风险 / 适用
堆分配(MEM_SIZE)类似 malloc 的连续堆灵活,适合变长报文长期运行碎片化,需配 MEM_LIBC_MALLOC 或定期统计
固定池(MEMP_NUM_xxx)定长 pbuf 池,编译期定个数分配 O(1),无碎片,实时确定性好池满即丢包,MEMP_NUM_TCP_PCB/PBUF_POOL_SIZE 要按并发连接数配
零拷贝(PBUF_ROM/PBUF_REF)数据不搬移,pbuf 仅引用应用缓冲大报文省一次拷贝与 RAM引用的数据生命周期必须覆盖发送完成回调,易出悬空引用

网关型产品的推荐组合:接收走 PBUF_POOL(DMA 描述符直接指向池块),发送走 PBUF_RAM,超大配置文件等静态数据走 PBUF_ROM。同时打开 LWIP_STATS:pbuf 分配失败次数、链路丢帧数、重传段数这三项指标进运行日志,是判断"内存吃紧还是网络拥塞"的第一证据。

七、代码与七步落地

以下为 STM32 + DM9161(MII 接口,详见本站 DM9161 PHY 接口文档)上 lwIP 无 OS 模式的初始化骨架:

/* lwIP 无OS模式初始化(STM32 以太网 + DM9161 PHY,MII 接口) */
#include "lwip/init.h"
#include "lwip/netif.h"
#include "netif/etharp.h"

static struct netif gnetif;

void lwip_stack_init(void)
{
    ip4_addr_t ip, netmask, gw;

    lwip_init();                              /* 核心初始化,NO_SYS=1 */

    IP4_ADDR(&ip,      192, 168, 1,  50);     /* 现场建议 DHCP 与静态双模式 */
    IP4_ADDR(&netmask, 255, 255, 255,  0);
    IP4_ADDR(&gw,      192, 168, 1,   1);

    netif_add(&gnetif, &ip, &netmask, &gw,
              NULL,                           /* state 传给网卡驱动 */
              ethernetif_init,                /* 底层:ETH MAC DMA + DM9161 读写 */
              ethernet_input);                /* 链路层入口,自动分流 ARP/IP */
    netif_set_default(&gnetif);
    netif_set_up(&gnetif);

    /* 主循环内周期调用(1~10ms):
       ethernetif_input(&gnetif);  收包搬运
       sys_check_timeouts();       ARP老化/重传/TCP定时  */
}

应用层用 socket 接口把 Modbus 采集的遥测按 MQTT 发上云(1883 端口、TCP 承载):

/* TCP socket:MQTT 风格遥测上云(简化示例) */
int telemetry_publish(void)
{
    int sock = socket(AF_INET, SOCK_STREAM, 0);      /* MQTT 固定走 TCP */
    if (sock < 0) return -1;

    struct sockaddr_in srv = {0};
    srv.sin_family      = AF_INET;
    srv.sin_port        = PP_HTONS(1883);            /* MQTT 默认端口 */
    srv.sin_addr.s_addr = inet_addr("192.168.1.100");

    if (connect(sock, (struct sockaddr *)&srv, sizeof(srv)) < 0) {
        close(sock);                                  /* 内核自动走完三次握手 */
        return -2;
    }
    /* 此处发送 MQTT CONNECT/PUBLISH 报文;keepalive 期间发 PINGREQ */
    /* 发送前可用 getsockopt(SO_SNDBUF) 检查 tcp_sndbuf 剩余空间 */
    send(sock, mqtt_publish_frame, mqtt_frame_len, 0);
    return 0;
}

把上面的机制收拢成七步落地清单:

步骤动作关键参数 / 判据
1 定模式裸机选 NO_SYS=1,RTOS 选线程模式有 RTOS 优先线程模式,socket 全套可用
2 接 PHYMII/RMII 接 DM9161,自协商 10/100M链路状态引脚或 BMSR 寄存器轮询
3 配内存接收走 PBUF_POOL,发送走 PBUF_RAMMEMP 统计零失败为达标
4 调 TCPtcp_mss=1460,tcp_wnd≥4×MSS吞吐不足先加大窗口再提主频
5 选传输Modbus/MQTT 走 TCP,心跳遥测可走 UDP指令不丢,毛刺可容
6 上应用MQTT keepalive 60~120s,断线指数退避重连PINGREQ 超时即重连,勿等 TCP 判死
7 留证据开 LWIP_STATS,丢包/重传/池失败进日志现场问题先看统计再看抓包

总结

MCU 以太网上云的捷径是"分层模型照抄、协议栈用 lwIP、力气花在内存与 TCP 参数":OSI 七层对应 TCP/IP 四层,lwIP 用 etharp/ip4/udp/tcp/apps 四组模块逐层实现;UDP 8 字节头无连接、TCP 20 字节头可靠有序,Modbus TCP 与 MQTT 走 TCP、高频心跳走 UDP;MSS 定 1460 字节、tcp_wnd 不小于 4 倍 MSS、pbuf 接收走固定池发送走堆并开启统计,网关就能在 10KB 级 RAM 上跑出稳定吞吐。按"定模式、接 PHY、配内存、调 TCP、选传输、上应用、留证据"七步执行,以太网链路问题都能定位到层。

本文基于《TCP/IP技术大全》(TCP/IP 分层模型、ARP/ICMP/TCP 协议机制等章节)等技术资料整理优化,并结合物联网网关以太网上云的工程实践进行了系统化梳理。

需要定制开发?

沧州艾诺威电子 — 国家高新技术企业,20+项国家专利
嵌入式系统开发 · 物联网方案 · AI智能硬件 · 一站式交付

立即微信咨询

电话:13930711029 | 邮箱:tech@czinv.com | 24小时内响应

🎧 本文已制作播客节目
双主持对话音频,随时随地收听本文内容
收听播客 →