3、嵌入式网络基础:TCP/IP协议栈分层、以太网帧、IP、TCP/UDP、ARP、ICMP核心概念

在嵌入式系统中移植LwIP之前,必须透彻理解TCP/IP协议栈的核心机制。LwIP(Lightweight IP)是一个为嵌入式系统设计的开源TCP/IP协议栈,它实现了对TCP/IP协议族的高度精简,但其核心架构与标准TCP/IP协议栈完全一致。本章将深入剖析协议栈的分层模型、以太网帧结构、IP协议、TCP/UDP传输层协议、ARP地址解析协议以及ICMP互联网控制报文协议,为后续的LwIP移植与调试打下坚实的理论基础。

TCP/IP协议栈分层与核心协议关系图 应用层 HTTP / MQTT / CoAP / TFTP / 自定义应用 LwIP API: netconn / socket 传输层 TCP(面向连接、可靠、字节流) UDP(无连接、不可靠、数据报) 端口号: 0-65535 网络层 IP(IPv4/IPv6)— 路由与寻址 ICMP — 差错报告与诊断(Ping) IP地址 + 路由表 链路层(网络接口层) 以太网帧封装 / 解封装 ARP — IP地址 ↔ MAC地址解析 MAC地址 + 以太网驱动 物理层 PHY芯片(如LAN8720、DP83848)/ 网线 / 电磁信号 MII / RMII 接口 发送:逐层向下封装(加头部) | 接收:逐层向上解封装(去头部)

3.1 TCP/IP协议栈分层模型与LwIP架构映射

3.1.1 核心原理:四层模型与数据封装

TCP/IP协议栈采用分层设计,每一层只负责特定的通信功能,层与层之间通过标准接口交互。在嵌入式领域,我们通常关注四层模型:应用层、传输层、网络层、链路层。数据发送时,每层会在上层数据前添加自己的头部信息(封装);接收时则逐层剥离头部(解封装)。LwIP的设计完全遵循这一模型,但其在实现上做了大量优化以适应嵌入式环境的内存限制。

LwIP的架构映射关系如下:

  • 应用层:LwIP提供三种API——raw/callback API(最底层,直接与协议栈交互)、netconn API(线程安全,基于信号量)、socket API(BSD风格,最易用)。
  • 传输层:tcp.c、udp.c 分别实现TCP和UDP协议,包含PCB(Protocol Control Block)管理、超时重传、流量控制等机制。
  • 网络层:ip.c 处理IP协议,icmp.c 处理ICMP,ip4_addr.c 管理IP地址。
  • 链路层:etharp.c 实现ARP,netif.c 管理网络接口,底层驱动通过 netif->linkoutput 函数指针与硬件交互。

3.1.2 判断方法:如何选择LwIP的API类型

在嵌入式项目中,API的选择直接影响系统架构和实时性。以下是判断依据:

API类型适用场景线程模型内存开销实时性
raw/callback裸机或RTOS下极简应用无阻塞,回调驱动最小最高
netconnRTOS环境,多线程应用阻塞式,内部使用mbox中等较高
socket需要BSD socket兼容性阻塞/非阻塞可选较大中等
经验提醒:在裸机环境中使用LwIP时,务必使用 raw API 并配合 sys_check_timeouts() 周期调用;若使用RTOS,推荐 netconn API,但需注意每个连接会占用一个线程栈空间(通常2-4KB)。

3.1.3 案例拆解:一个HTTP请求的封装旅程

假设嵌入式设备(IP: 192.168.1.100)向服务器(IP: 192.168.1.1)发起HTTP GET请求,数据封装过程如下:

  1. 应用层:构造HTTP报文 "GET /index.html HTTP/1.1\r\nHost: 192.168.1.1\r\n\r\n",交给LwIP的netconn API。
  2. 传输层:TCP模块添加20字节TCP头部(源端口1024,目的端口80,序列号,校验和等),形成TCP段。
  3. 网络层:IP模块添加20字节IP头部(源IP 192.168.1.100,目的IP 192.168.1.1,协议号6表示TCP),形成IP数据报。
  4. 链路层:ARP模块查询目的IP对应的MAC地址(假设为 00:11:22:33:44:55),以太网驱动添加14字节以太网帧头(目的MAC、源MAC、类型0x0800),并追加4字节FCS校验。

最终在网线上传输的完整帧长度为:14(以太网头)+ 20(IP头)+ 20(TCP头)+ HTTP报文长度 + 4(FCS)。

3.2 以太网帧结构与MAC寻址

3.2.1 核心原理:以太网帧格式

IEEE 802.3标准定义了以太网帧结构,LwIP的 etharp.c 和网络驱动直接处理该格式。标准帧结构如下:

| 前导码(7B) | SFD(1B) | 目的MAC(6B) | 源MAC(6B) | 类型/长度(2B) | 载荷(46-1500B) | FCS(4B) |
  • 前导码与SFD:物理层同步用,LwIP不处理。
  • 目的/源MAC:6字节,唯一标识网卡。LwIP通过 netif->hwaddr 配置本机MAC。
  • 类型/长度字段:若值大于0x0600(1536),表示上层协议类型(0x0800=IPv4,0x0806=ARP,0x86DD=IPv6);若小于0x0600,表示载荷长度。
  • 载荷:最小46字节,最大1500字节(MTU)。若IP包小于46字节,需填充。
  • FCS:CRC32校验,由硬件计算,LwIP不参与。

3.2.2 判断方法:识别帧类型与VLAN标签

在抓包分析时,判断帧类型的关键是查看类型字段:

类型值协议LwIP处理模块
0x0800IPv4ip4_input()
0x0806ARPetharp_input()
0x86DDIPv6ip6_input()(需开启LWIP_IPV6)
0x8100VLAN Tag需驱动支持,LwIP默认不解析
风险提示:若网络中存在VLAN交换机,LwIP默认无法处理带VLAN标签的帧。此时需在底层驱动中剥离VLAN头,或将 ETH_P_8021Q 注册到LwIP的输入分发函数中,否则会导致丢包。

3.2.3 实战场景:MAC地址配置与冲突检测

在STM32+LAN8720方案中,MAC地址通常存储在外部EEPROM或OTP区域。配置流程:

  1. 读取MAC地址(如 0x00, 0x80, 0xE1, 0x12, 0x34, 0x56)。
  2. 调用 netif->hwaddr_len = 6 和 netif->hwaddr[] 赋值。
  3. 调用 netif_set_up(netif) 激活接口。

若MAC地址全为0或全为1,会导致网络通信异常。建议在调试时使用Wireshark抓包,检查源MAC是否为预期值。

3.3 IP协议:寻址、分片与路由

3.3.1 核心原理:IPv4头部与关键字段

IPv4头部固定20字节(无选项字段时),LwIP的 ip4.h 中定义了 ip_hdr 结构体。关键字段包括:

  • 版本/头部长度(4+4位):版本固定为4,头部长度以4字节为单位,最小值为5(20字节)。
  • 总长度(16位):IP包总字节数,最大65535。LwIP中 IP_REASS_MAX_PDU 控制重组缓冲区大小。
  • 标识/标志/片偏移(16+3+13位):用于IP分片。LwIP默认支持分片重组,但需开启 LWIP_IP_REASSEMBLY。
  • TTL(8位):每经过一个路由器减1,为0时丢弃。LwIP默认值 IP_DEFAULT_TTL 为255。
  • 协议(8位):1=ICMP,6=TCP,17=UDP。
  • 头部校验和(16位):仅校验IP头部,LwIP通过 inet_chksum() 计算。

3.3.2 判断方法:IP地址配置与子网掩码计算

嵌入式设备通常使用静态IP或DHCP。静态配置时需注意:

IP4_ADDR(&ipaddr, 192, 168, 1, 100);   // 设置IP
IP4_ADDR(&netmask, 255, 255, 255, 0); // 子网掩码
IP4_ADDR(&gw, 192, 168, 1, 1);        // 默认网关
netif_set_addr(netif, &ipaddr, &netmask, &gw);

判断两个IP是否在同一子网:将IP与子网掩码按位与,结果相同则在同一子网。例如:

  • 设备A:192.168.1.100 & 255.255.255.0 = 192.168.1.0
  • 设备B:192.168.1.200 & 255.255.255.0 = 192.168.1.0 → 同一子网,直接ARP通信。
  • 若目标IP为192.168.2.1,则需通过网关转发。

3.3.3 案例拆解:跨网段通信的IP路由决策

嵌入式设备(192.168.1.100/24)需要访问服务器(192.168.2.50/24)。LwIP的 ip_route() 函数决策流程:

  1. 检查目标IP是否与任一 netif 在同一子网 → 否。
  2. 查找默认网关(192.168.1.1)→ 存在。
  3. 将数据包交给网关的MAC地址(通过ARP获取),IP头部中的目的IP保持192.168.2.50不变。
  4. 网关路由器负责转发到192.168.2.0/24网段。
调试要点:若设备无法跨网段通信,首先 ping 192.168.1.1 验证网关可达性;然后检查 netif->gw 是否配置正确;最后用Wireshark抓包确认ARP请求是否发往网关。

3.4 TCP与UDP:传输层协议对比与LwIP实现

3.4.1 核心原理:TCP可靠传输与UDP高效传输

TCP提供面向连接的可靠字节流服务,其核心机制包括:

  • 三次握手:SYN → SYN+ACK → ACK,LwIP通过 tcp_connect() 发起,状态机在 tcp.c 中管理。
  • 滑动窗口:接收方通告窗口大小,LwIP的 tcp_sndbuf() 返回当前发送缓冲区剩余空间。
  • 超时重传:RTO(重传超时)动态计算,LwIP使用 TCP_TMR_INTERVAL(默认250ms)作为定时粒度。
  • 拥塞控制:慢启动、拥塞避免、快速重传/恢复。LwIP默认开启 LWIP_TCP_CONGESTION_CONTROL。

UDP则无连接、无可靠性保证,头部仅8字节(源端口、目的端口、长度、校验和),适合实时音视频、DNS查询等场景。

3.4.2 对比分析:TCP与UDP的选择依据

维度TCPUDP
连接状态需建立/断开连接无连接
可靠性确认、重传、排序尽力而为
数据边界字节流,无边界数据报,保留边界
头部开销20-60字节8字节
LwIP内存占用每个连接约2-4KB PCB每个PCB约0.5KB
典型应用HTTP、MQTT、文件传输SNMP、DHCP、实时控制

3.4.3 实战场景:LwIP TCP服务器内存优化

在资源受限的MCU(如STM32F103,RAM 20KB)上运行TCP服务器,需精细配置 lwipopts.h:

#define MEM_SIZE          (10 * 1024)  // 堆内存池
#define TCP_SND_BUF       (2 * 1024)   // 发送缓冲区
#define TCP_WND           (2 * 1024)   // 接收窗口
#define TCP_MSS           1460          // 最大段大小
#define PBUF_POOL_SIZE    10            // pbuf池数量

若同时支持2个客户端连接,每个连接占用约3KB(PCB+缓冲区),加上协议栈基础开销,总内存约15KB,留有余量。

常见误区:不要将 TCP_MSS 设置为大于网卡MTU(1500)的值,否则会导致IP分片,降低性能并增加内存消耗。正确做法是 TCP_MSS = MTU - 40(IPv4+TCP头部)。

3.5 ARP协议:IP到MAC的地址解析

3.5.1 核心原理:ARP请求/响应与缓存

ARP用于在同一广播域内将IP地址解析为MAC地址。LwIP的 etharp.c 维护一个ARP缓存表(默认 ARP_TABLE_SIZE 为10)。工作流程:

  1. 发送数据前,查询ARP缓存表。
  2. 若未命中,构造ARP请求帧(广播,目的MAC为FF:FF:FF:FF:FF:FF),携带目标IP。
  3. 目标主机收到后,回复ARP应答(单播),包含其MAC地址。
  4. LwIP更新缓存表,并设置超时时间(默认 ARP_MAXAGE 为5分钟)。

3.5.2 判断方法:ARP缓存表查看与排障

在LwIP调试中,可通过以下方式检查ARP状态:

  • 调用 etharp_find_addr() 查询指定IP的MAC。
  • 使用 etharp_tmr() 周期调用(每1秒)处理超时条目。
  • 在RTOS中,可通过 netif->linkoutput 回调打印ARP帧内容。

常见ARP问题:

现象可能原因排查方法
ping不通但抓包有ARP请求目标主机未响应或不在同一子网检查IP/掩码配置
ARP缓存表频繁刷新ARP攻击或网络环路检查交换机端口配置
首次通信延迟大ARP缓存未命中,需等待超时预填充ARP表或使用静态ARP

3.5.3 案例拆解:静态ARP表加速工业通信

在工业现场总线场景中,PLC(192.168.0.10)与嵌入式设备(192.168.0.20)需要毫秒级响应。为避免ARP解析延迟,可在初始化时预填充ARP表:

struct eth_addr mac = {{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}};
ip4_addr_t ip;
IP4_ADDR(&ip, 192, 168, 0, 10);
etharp_add_static_entry(&ip, &mac);  // 添加静态条目

这样首次通信无需ARP请求,直接发送数据帧。但需注意:若PLC更换网卡,静态条目需同步更新,否则通信中断。

3.6 ICMP协议:差错报告与网络诊断

3.6.1 核心原理:ICMP报文类型与LwIP实现

ICMP主要用于网络层差错报告和诊断。LwIP的 icmp.c 主要处理两类报文:

  • Echo请求/应答(类型8/0):即Ping功能。LwIP收到Echo请求后,自动回复Echo应答。
  • 目的不可达(类型3):当端口不可达或网络不可达时发送。

ICMP报文封装在IP数据报中,头部格式:类型(1字节)、代码(1字节)、校验和(2字节)、标识(2字节)、序列号(2字节)、数据。

3.6.2 判断方法:Ping通但TCP连接失败的分析

这是一个经典排障场景:

  1. Ping通:说明IP层、链路层、ARP均正常,物理链路无问题。
  2. TCP连接失败:可能原因包括:
    • 目标端口未监听(服务器未启动)→ 收到ICMP端口不可达。
    • 防火墙过滤TCP SYN包 → 无任何响应。
    • LwIP的 TCP_MSS 协商失败 → 连接建立后立即断开。
经验提醒:在LwIP中,若收到ICMP目的不可达报文,协议栈会调用 tcp_err() 回调函数。务必在该回调中处理错误状态,避免连接悬挂。

3.6.3 实战场景:自定义ICMP扩展实现设备探测

某些工业协议需要设备主动发送ICMP时间戳请求(类型13)来同步时钟。LwIP默认不支持,需扩展:

// 在 icmp_input() 中添加 case 13 处理
case ICMP_TSTAMP:
  // 构造时间戳应答(类型14)
  // 填充原始时间戳、接收时间戳、发送时间戳
  // 调用 ip4_output_if() 发送
  break;

此扩展需注意字节序(网络序)和校验和计算,建议参考RFC 792实现。

3.7 常见误区与总结复盘

3.7.1 五大常见误区

误区后果正确做法
忽略MAC地址唯一性网络冲突,通信随机失败从IEEE OUI池分配或使用MCU唯一ID生成
TCP_MSS设置过大IP分片,性能下降MSS = MTU - 40(IPv4)或 -60(IPv6)
ARP缓存表过小频繁ARP请求,延迟增加根据设备数量设置 ARP_TABLE_SIZE
UDP校验和关闭数据静默损坏保持 CHECKSUM_GEN_UDP 为1
ICMP回调未处理连接悬挂,资源泄漏在 tcp_err() 中释放连接资源

3.7.2 总结复盘:协议栈调试方法论

当网络通信异常时,建议按以下顺序排查:

  1. 物理层:检查网线连接、PHY芯片LED状态、link状态寄存器。
  2. 链路层:抓包查看以太网帧是否正常,源/目的MAC是否正确。
  3. 网络层:Ping网关验证IP配置,检查路由表。
  4. 传输层:使用TCP测试工具(如NetAssist)验证端口连通性。
  5. 应用层:检查应用协议格式是否符合RFC规范。
核心要点:LwIP的移植调试本质上是分层验证的过程。每一层只依赖下一层的服务,因此务必从底层向上逐层排查。熟练掌握以太网帧、IP、TCP/UDP、ARP、ICMP的报文格式和交互流程,是快速定位问题的关键。