2. emNet源码包结构与关键文件解析
拿到 emNet 发布包后,第一件不该做的事是急着打开某个 .c 文件逐行阅读。emNet 是一个按协议分层、按功能分目录组织的嵌入式 TCP/IP 协议栈,如果缺少一张“源码地图”,很容易在几十个源文件之间迷路。本节的目标是:先看清发布包的目录骨架,再定位 IP、TCP、UDP、ICMP、ARP、DHCP、DNS 等模块各自落在哪个文件里,最后理解这些文件之间的调用与数据流关系,为后续的移植、配置和裁剪打下基础。
一、发布包的顶层目录组织
emNet 的发布包通常以 SEGGER/emNet/ 为根,下面按“通用源码 + 端口层 + 配置 + 文档 + 示例”几个维度展开。不同版本目录名可能略有差异,但结构逻辑基本一致。典型布局如下:
emNet/
├── IP/ # 协议栈核心源码(与硬件无关)
│ ├── IP.c / IP.h
│ ├── TCP.c / TCP.h
│ ├── UDP.c / UDP.h
│ ├── ICMP.c / ICMP.h
│ ├── ARP.c / ARP.h
│ ├── DHCPc.c / DHCPc.h
│ ├── DNS.c / DNS.h
│ ├── IGMP.c / IGMP.h
│ ├── IP_Config.h
│ └── ...
├── Config/ # 用户配置与端口相关配置
│ ├── IP_Config.c
│ ├── IP_Config.h
│ └── ...
├── PORT/ # 硬件/RTOS 移植层
│ ├── IP_OS.c
│ ├── IP_NI_*.c # 网络接口驱动适配
│ └── ...
├── Sample/ # 示例应用(如 WebServer、TCP 回显)
├── Doc/ # 手册与 API 文档
└── SEGGER/ # 依赖的 SEGGER 基础组件(如 RTOS、调试)
这里最关键的分界是:IP/ 目录下的文件与硬件无关,它们只依赖标准 C 和 emNet 内部接口;PORT/ 目录下的文件与硬件和 RTOS 相关,负责把协议栈“接”到具体的以太网控制器和操作系统上。理解这条分界线,是判断“某个文件该不该改”的第一依据。
二、协议模块与文件的对应关系
emNet 把每个协议族基本收敛到一对 .c/.h 文件中,命名直观。下表给出常见模块与文件的对应,以及各自的核心职责:
| 模块 | 主要文件 | 核心职责 |
|---|---|---|
| IP 层 | IP.c / IP.h | IP 首部处理、分片重组、路由选择、上层协议分发 |
| TCP | TCP.c / TCP.h | 连接状态机、滑动窗口、重传、序号确认、端口管理 |
| UDP | UDP.c / UDP.h | 无连接数据报收发、端口复用、校验和 |
| ICMP | ICMP.c / ICMP.h | 回显请求/应答(ping)、差错报文生成 |
| ARP | ARP.c / ARP.h | IP→MAC 地址解析、ARP 缓存维护 |
| DHCP 客户端 | DHCPc.c / DHCPc.h | 动态获取 IP、掩码、网关、DNS 地址 |
| DNS 客户端 | DNS.c / DNS.h | 域名到 IP 的解析请求与响应处理 |
| IGMP | IGMP.c / IGMP.h | 组播组成员管理(可选模块) |
需要特别注意的是 IP_Config.h 与 IP_Config.c。前者集中了大量编译期开关,例如是否启用 TCP、UDP、DHCP、DNS、IGMP,缓冲区数量、最大连接数、是否支持分片等;后者通常存放用户可修改的运行时配置,如本机 MAC、默认 IP、子网掩码、网关。裁剪协议栈时,第一步就是改这两个文件,而不是去删 .c 文件。
三、模块之间的调用与数据流
从接收方向看,数据从网络接口驱动进入,经 IP 层解析后按协议号分发:协议号 6 交给 TCP,17 交给 UDP,1 交给 ICMP。发送方向则相反,应用通过 socket 风格 API 调用 TCP/UDP,再经 IP 层封装,最后由网络接口发出。ARP 与 IP 是“旁路”关系:IP 在发送前需要目的 MAC,若 ARP 缓存未命中,会先挂起该包并发起 ARP 请求。DHCP 和 DNS 本身是应用层协议,但它们通过 UDP 承载,因此依赖 UDP 与 IP 正常工作。
四、移植层与配置文件的定位
PORT/ 目录是 emNet 与具体硬件之间的“适配器”。其中 IP_NI_*.c 系列文件对应不同的网络接口控制器(NI,Network Interface),例如内部 MAC、外部以太网芯片等。移植时通常需要实现或修改:网卡初始化、发送一帧、接收一帧、中断处理、以及可选的校验和卸载。另一个关键文件是 IP_OS.c,它把 emNet 内部的信号量、互斥量、任务延时等抽象映射到实际 RTOS(如 embOS、FreeRTOS 或裸机)。如果使用裸机,则这些函数通常退化为简单的标志位或空实现。
Config/ 目录下的 IP_Config.h 是裁剪协议栈的“总开关”。例如,若产品不需要 DNS,可将 IP_USE_DNS 置 0,链接时 DNS.c 的目标代码就不会被引用,从而节省 Flash 和 RAM。类似地,IP_USE_DHCP、IP_USE_TCP、IP_USE_UDP 等宏控制各模块的编译。需要强调的是:关闭某个宏后,应用层就不能再调用对应 API,否则会出现链接错误或运行时异常,因此裁剪必须与应用程序同步调整。
五、建立源码地图的实用方法
面对一个具体的 emNet 版本,建议按以下顺序建立自己的源码地图:
- 先列出 IP/ 目录下所有 .c/.h 文件,按协议名归类,确认哪些模块被当前配置启用。
- 打开
IP_Config.h,记录所有IP_USE_*宏的当前取值,形成一张“启用模块清单”。 - 在 PORT/ 目录中确认网络接口驱动文件名,找到与目标硬件匹配的那一个,并记录它依赖的寄存器或外设。
- 从应用入口(如 Sample/ 中的示例)出发,沿 API 调用向下追踪一次完整收发路径,验证自己对分层关系的理解。
- 把上述信息整理成一页纸的“源码地图”,标注每个文件的职责、是否可修改、依赖关系。