lwIP TCP/IP 协议栈
杰理 AC79 系列 AIoT SDK 基于 lwIP 2.2.0 构建的 TCP/IP 协议栈移植层,封装了多网络接口(Wi-Fi / 有线以太网 / 蓝牙 / LTE / 扩展无线)的初始化、DHCP、事件回调与 BSD Socket 编程接口,为上层应用提供统一的联网能力。
Purpose and Scope
本页面向 SDK 中的 lwIP TCP/IP 协议栈移植与集成层,涵盖:
- lwIP 2.2.0 在 AC79 平台上的源码与头文件布局(
lib/net/lwip_2_2_0与include_lib/net/lwip_2_2_0); - 移植层核心文件
LwIP.c的多 netif 管理与初始化逻辑; - 各网络接口驱动(
wireless_ethernetif、wired_ethernetif、bt_ethernetif、lte_ethernetif、ext_wireless_ethernetif、wireless_raw_ethernetif); lwipopts.h的裁剪配置、LWIP_EVENT事件机制、Socket API 及上层应用模块(NTP、DHCP Server、mSSDP 等)。
不属于本页范围(由同级页面覆盖):Wi-Fi 连接管理、蓝牙协议栈、LTE 模组 AT 指令、HTTP/curl 应用层等,本页仅在其调用 lwIP 接口时提及。
概述(Overview)
lwIP(lightweight IP)是专为嵌入式系统设计的开源 TCP/IP 协议栈,以"小内存占用 + 完整 TCP/IP 功能"为目标。本 SDK 选用 lwIP 2.2.0,并将其移植到 AC79 芯片平台,形成如下关键设计:
- 多网络接口抽象:同一套协议栈可同时挂载 Wi-Fi、有线以太网(ETH)、蓝牙(BT)、LTE、扩展无线(EXT_WIFI)、无线 RAW 共 6 种 netif,每种接口由
lwip.h中的宏编号唯一标识(ETH_NETIF=0…WIFI_RAW_NETIF=5); - 静态局域网配置表:每种 netif 对应一份
struct lan_setting,内含 IP、掩码、网关、DHCP 服务器地址与客户端起始地址等参数,编译期即可裁剪(HAVE_*_NETIF宏); - DHCP 超时与事件回调:
LwIP.c内以 100ms 定时器驱动 DHCP 流程,绑定成功/超时通过LWIP_EVENT枚举通知上层,应用通过覆盖 weak 符号xxx_lwip_event_cb感知网络状态; - BSD Socket API:
LWIP_SOCKET=1且LWIP_COMPAT_SOCKETS=1,应用可直接使用socket/connect/send/recv等标准接口;curl 等库通过USE_LWIPSOCK直接调用lwip_init()。
典型使用场景:Wi-Fi 连上 AP 后调用 Init_LwIP(WIFI_NETIF) 获取 IP,再基于 Socket 发起 HTTP/MQTT 连接;BT 网络接口用于手机共享网络;LTE 接口用于蜂窝数据上网。
架构(Architecture)
flowchart TD
subgraph sg_App["应用层"]
App["应用任务 (socket 调用)"]
Curl["curl (USE_LWIPSOCK)"]
end
subgraph sg_API["API 层"]
SockAPI["BSD Socket API<br/>(sockets.c)"]
NetDB["DNS / netdb"]
end
subgraph sg_Core["lwIP 核心"]
Core["TCP / UDP / IP / ICMP / ARP"]
NetifCore["netif 抽象层"]
DHCP["DHCP 客户端"]
IP6["IPv6 / DHCPv6"]
end
subgraph sg_Port["AC79 移植层 (port)"]
LWIPC["LwIP.c 多 netif 管理"]
WNetif["wireless_ethernetif"]
ENetif["wired_ethernetif"]
BNetif["bt_ethernetif"]
LNetif["lte_ethernetif"]
XNetif["ext_wireless_ethernetif"]
RNetif["wireless_raw_ethernetif"]
Opts["lwipopts.h / sys_arch.h"]
end
subgraph sg_Drv["硬件/驱动层"]
WiFiDrv["Wi-Fi 驱动"]
EthDrv["ETH PHY"]
BtDrv["蓝牙驱动"]
LteDrv["LTE 模组"]
end
App --> SockAPI
Curl --> SockAPI
SockAPI --> Core
NetDB --> Core
Core --> NetifCore
NetifCore --> LWIPC
DHCP --> NetifCore
IP6 --> NetifCore
LWIPC --> WNetif
LWIPC --> ENetif
LWIPC --> BNetif
LWIPC --> LNetif
LWIPC --> XNetif
LWIPC --> RNetif
Opts -.->|"编译期裁剪"| Core
WNetif --> WiFiDrv
ENetif --> EthDrv
BNetif --> BtDrv
LNetif --> LteDrv
XNetif --> WiFiDrv
架构说明:上层应用(含 curl)只面对 BSD Socket API;协议栈核心(TCP/UDP/IP/ARP)之上由 netif 抽象层屏蔽具体物理介质差异。AC79 移植层 LwIP.c 负责创建并注册 6 种 netif,每种 netif 的 init 函数(如 wireless_ethernetif_init)完成驱动绑定与 MAC/主机名设置。lwipopts.h 在编译期裁剪协议栈功能(如是否启用 Socket、IPv6、窗口缩放),这是嵌入式内存优化的关键手段。sys_arch.h 提供 OS 抽象(信号量、邮箱、线程),使 lwIP 能运行在 RTOS 之上。
核心实现分析
1. 移植层入口:lib/net/lwip_2_2_0/port/LwIP.c
LwIP.c 是整条移植链的枢纽,职责包括:声明 6 种 netif 的 init 函数、维护每接口独立的 struct lan_setting 静态配置、提供 Init_LwIP / lwip_netif_set_up / lwip_netif_set_down / lwip_renew 等对外 API,并以 100ms 周期推进 DHCP 超时检测。
文件顶部通过宏开关决定编译哪些 netif:
#define HAVE_ETH_WIRE_NETIF
#define HAVE_LTE_NETIF
#define HAVE_BT_NETIF
#define HAVE_EXT_WIRELESS_NETIF
#define HAVE_WRIELESS_RAW_NETIF
extern err_t wireless_ethernetif_init(struct netif *netif);
extern err_t ext_wireless_ethernetif_init(struct netif *netif);
extern err_t wired_ethernetif_init(struct netif *netif);
extern err_t bt_ethernetif_init(struct netif *netif);
extern err_t lte_ethernetif_init(struct netif *netif);
extern err_t wireless_raw_ethernetif_init(struct netif *netif);
Source: LwIP.c
这些 err_t xxx_ethernetif_init(struct netif *) 是 lwIP 标准的 netif 初始化回调,分别实现在 port/ 目录下各自的 xxx_ethernetif.c 文件中。LwIP.c 通过 extern 声明引用它们,在运行时把它们注册进 netif_add()。
2. 静态局域网配置表 struct lan_setting
每种网络接口在编译期内置一份默认 IP 配置。结构体定义在 lwip.h:
struct lan_setting {
u8_t WIRELESS_IP_ADDR0;//无线IP地址
u8_t WIRELESS_IP_ADDR1;
u8_t WIRELESS_IP_ADDR2;
u8_t WIRELESS_IP_ADDR3;
u8_t WIRELESS_NETMASK0;//无线掩码
u8_t WIRELESS_NETMASK1;
u8_t WIRELESS_NETMASK2;
u8_t WIRELESS_NETMASK3;
u8_t WIRELESS_GATEWAY0;//无线网关
u8_t WIRELESS_GATEWAY1;
u8_t WIRELESS_GATEWAY2;
u8_t WIRELESS_GATEWAY3;
u8_t SERVER_IPADDR1;//DHCP服务器地址
u8_t SERVER_IPADDR2;
u8_t SERVER_IPADDR3;
u8_t SERVER_IPADDR4;
u8_t CLIENT_IPADDR1;//起始IP地址
u8_t CLIENT_IPADDR2;
u8_t CLIENT_IPADDR3;
u8_t CLIENT_IPADDR4;
u8_t SUB_NET_MASK1;//子网掩码
u8_t SUB_NET_MASK2;
u8_t SUB_NET_MASK3;
u8_t SUB_NET_MASK4;
};
Source: lwip.h
设计意图:字段名虽以 WIRELESS_ 开头,但被所有接口复用——各接口以不同初值填充同一结构。Wi-Fi 与蓝牙默认 192.168.1.1 / 192.168.0.1 网段,有线以太网默认 172.16.109.152,保证多接口共存时网段不冲突;SERVER_IPADDR 与 CLIENT_IPADDR 用于内置 DHCP Server(app/dhcp_srv)的地址池分配。每个接口的实例定义于 LwIP.c,例如:
static struct lan_setting wireless_lan_setting_info = {
.WIRELESS_IP_ADDR0 = 192,
.WIRELESS_IP_ADDR1 = 168,
.WIRELESS_IP_ADDR2 = 1,
.WIRELESS_IP_ADDR3 = 1,
.WIRELESS_NETMASK0 = 255,
.WIRELESS_NETMASK1 = 255,
.WIRELESS_NETMASK2 = 255,
.WIRELESS_NETMASK3 = 0,
.WIRELESS_GATEWAY0 = 192,
.WIRELESS_GATEWAY1 = 168,
.WIRELESS_GATEWAY2 = 1,
.WIRELESS_GATEWAY3 = 1,
.SERVER_IPADDR1 = 192,
.SERVER_IPADDR2 = 168,
.SERVER_IPADDR3 = 1,
.SERVER_IPADDR4 = 1,
.CLIENT_IPADDR1 = 192,
.CLIENT_IPADDR2 = 168,
.CLIENT_IPADDR3 = 1,
.CLIENT_IPADDR4 = 101,
.SUB_NET_MASK1 = 255,
.SUB_NET_MASK2 = 255,
.SUB_NET_MASK3 = 255,
.SUB_NET_MASK4 = 0,
};
Source: LwIP.c
net_get_lan_info(u8_t lwip_netif) 按 netif 编号返回对应配置表,供初始化与 DHCP Server 查询;net_get_netif_handle(u8_t lwip_netif) 返回对应 struct netif * 句柄。
3. 网络接口编号与事件枚举(lwip.h)
#define ETH_NETIF (0)
#define WIFI_NETIF (1)
#define BT_NETIF (2)
#define LTE_NETIF (3)
#define EXT_WIFI_NETIF (4)
#define WIFI_RAW_NETIF (5)
#define MAX_NETIF_NUM (6)
enum LWIP_EVENT {
LWIP_WIRELESS_DHCP_BOUND_TIMEOUT,
LWIP_WIRELESS_DHCP_BOUND_SUCC,
LWIP_WIRE_DHCP_BOUND_TIMEOUT,
LWIP_WIRE_DHCP_BOUND_SUCC,
LWIP_LTE_DHCP_BOUND_TIMEOUT,
LWIP_LTE_DHCP_BOUND_SUCC,
LWIP_EXT_WIRELESS_DHCP_BOUND_TIMEOUT,
LWIP_EXT_WIRELESS_DHCP_BOUND_SUCC,
LWIP_BT_DHCP_BOUND_TIMEOUT,
LWIP_BT_DHCP_BOUND_SUCC,
LWIP_IP_GOT_IPV6_SUCC,
};
LWIP_EVENT 是移植层向上层广播网络状态的事件集合:每个接口的 DHCP 绑定成功/超时各占两个枚举值,另有一个 IPv6 获取成功事件。事件通过 weak 回调(见下文)分发,应用可选择性覆盖其中任意一个而不影响其它接口。
4. Weak 符号回调:应用层的扩展点
LwIP.c 定义了 5 个 __attribute__((weak)) 回调,默认实现为空函数(返回 0),应用层可定义同名强符号覆盖:
int __attribute__((weak)) lwip_event_cb(void *lwip_ctx, enum LWIP_EVENT event)
{
return 0;
}
int __attribute__((weak)) lte_lwip_event_cb(void *lwip_ctx, enum LWIP_EVENT event)
{
return 0;
}
int __attribute__((weak)) ext_lwip_event_cb(void *lwip_ctx, enum LWIP_EVENT event)
{
return 0;
}
int __attribute__((weak)) eth_lwip_event_cb(void *lwip_ctx, enum LWIP_EVENT event)
{
return 0;
}
int __attribute__((weak)) bt_lwip_event_cb(void *lwip_ctx, enum LWIP_EVENT event)
{
return 0;
}
Source: LwIP.c
同一文件还提供两个 weak 扩展钩子:dns_set_server(unsigned int *dnsserver)(默认填 0x23415679 占位)与 socket_send_but_netif_busy_hook(int s, char type_udp)(网络忙时的发送拦截钩子)。这种"weak 默认 + 强符号覆盖"模式在嵌入式 SDK 中非常常见:既保证链接期无未定义符号,又免去了虚函数表开销。
5. DHCP 驱动与超时机制
LwIP.c 定义了 DHCP_TMR_INTERVAL 100(毫秒)与 dhcp_timeout_msec = 15 * 1000(15 秒),并为 Wi-Fi/ETH/LTE/EXT/BT 各维护一个 xxx_dhcp_timeout_cnt 计数器与 static u8 lwip_static_ip_renew[MAX_NETIF_NUM] 静态 IP 续租标志数组。核心思路是:
- 调用
lwip_renew(lwip_netif, dhcp)启动 DHCP(dhcp==1)或静态 IP(dhcp==0); - 周期定时(100ms)检查是否超时;超时后若开启
IPV4_ADDR_CONFLICT_DETECT则进行 IP 冲突检测,并向上层派发LWIP_*_DHCP_BOUND_TIMEOUT事件; - 绑定成功则派发
LWIP_*_DHCP_BOUND_SUCC,同时lwip_etharp_cleanup_netif()清理 ARP 表,防止旧邻居缓存污染新网络。
6. 网络接口驱动(port/*_ethernetif.c)
每种物理介质对应一个 netif 驱动,遵循 lwIP 的 netif->linkoutput / output / init 回调模型。以 bt_ethernetif.c 为例,初始化时会设置接口主机名:
#if LWIP_NETIF_HOSTNAME
/* Initialize interface hostname */
netif->hostname = LWIP_LOCAL_HOSTNAME;
#endif
Source: bt_ethernetif.c
lte_ethernetif.c 额外在 LWIP_NETIF_REMOVE_CALLBACK 开启时注册 remove_callback = lte_ethernetif_exit,保证 netif 被移除(如 LTE 模组断线)时能正确释放驱动资源——这是多接口热插拔场景的关键清理路径。各驱动的 linkoutput 最终把 lwIP 的 pbuf 链交给对应硬件驱动(Wi-Fi/ETH PHY/蓝牙/LTE 模组)发送。
7. 编译期裁剪:lwipopts.h
include_lib/net/lwip_2_2_0/lwip/port/lwipopts.h 是 lwIP 的选项配置文件,直接决定协议栈的内存占用与功能集合,关键项:
//开启支持IPV6
#define CONFIG_LWIP_IPV6_ENABLE
#define MULTI_NETIF_SUPPORT 0
#define JL_LWIP 1
#define LWIP_HAVE_LOOPIF 1
#define LWIP_SOCKET 1
#define LWIP_COMPAT_SOCKETS 1
#define LWIP_NETCONN 0
#define LWIP_SO_RCVTIMEO 1
#define LWIP_SO_SNDTIMEO 1
#define LWIP_SO_SNDRCVTIMEO_NONSTANDARD 1
#define LWIP_WND_SCALE 0
#define TCP_RCV_SCALE 0
#define DHCPS_UNICAST_ENABLE 0
#define ETHARP_SUPPORT_STATIC_ENTRIES 1
#define DEFAULT_ACCEPTMBOX_SIZE 60
#define DEFAULT_RAW_RECVMBOX_SIZE 61
Source: lwipopts.h
设计权衡:Socket API 开启而 Netconn API 关闭(LWIP_SOCKET=1, LWIP_NETCONN=0),因为上层(curl、MQTT 等)只使用 BSD 风格接口,省去 netconn 层的内存与线程开销;LWIP_COMPAT_SOCKETS=1 让 sockets.h 直接提供 socket/connect 等标准名称;SO_RCVTIMEO/SNDTIMEO 以毫秒 int 形式(非标准模式)提供,便于嵌入式应用设置超时;窗口缩放关闭以节省每连接内存;DEFAULT_ACCEPTMBOX_SIZE=60 保证 listen 积压队列容量。另外提供 lwipopts_bt.h、lwipopts_sfc.h 等变体,供不同产品配置复用。
核心流程(Core Flow)
Wi-Fi 接口联网全流程
以最常见的 Wi-Fi 场景为例,展示从协议栈启动到应用拿到 IP 的完整时序:
sequenceDiagram
participant App as 应用任务
participant LWIPC as LwIP.c 移植层
participant CORE as lwIP 核心 (tcpip)
participant DRV as Wi-Fi 驱动
participant AP as AP/路由器 (DHCP Server)
App->>LWIPC: Init_LwIP(WIFI_NETIF)
activate LWIPC
LWIPC->>CORE: lwip_init() / netif_add(wireless_ethernetif_init)
CORE->>DRV: wireless_ethernetif_init(netif)
DRV-->>CORE: netif 注册成功 (MAC/hostname)
LWIPC->>CORE: lwip_renew(WIFI_NETIF, dhcp=1)
CORE->>AP: DHCP Discover
AP-->>CORE: DHCP Offer
CORE->>AP: DHCP Request
AP-->>CORE: DHCP Ack (绑定 IP)
CORE->>LWIPC: DHCP 绑定成功
LWIPC->>App: lwip_event_cb(LWIP_WIRELESS_DHCP_BOUND_SUCC)
deactivate LWIPC
App->>CORE: socket()/connect()/send() 业务通信
关键点说明:
Init_LwIP(WIFI_NETIF)是每个接口的入口,内部保证lwip_init()只执行一次(static u8 lwip_inited防重入);netif_add()传入wireless_ethernetif_init,驱动回调里填充netif->hwaddr(MAC 来自netdev_get_mac_addr())、hostname与linkoutput;lwip_renew选择 DHCP(默认use_dhcp=1)或静态 IP;DHCP 由 lwIP 的dhcp_start()驱动,移植层只负责 100ms 超时轮询;- 绑定结果经
LWIP_EVENT回调上抛,Wi-Fi 事件走lwip_event_cb,LTE/EXT/BT/ETH 各有独立回调,避免多接口事件互相干扰。
DHCP 状态机与超时分支
flowchart TD
Start([lwip_renew]) --> IsDhcp{" dhcp 参数?"}
IsDhcp -->|"dhcp=0 静态IP"| Static["netif_set_addr 静态配置"]
IsDhcp -->|"dhcp=1"| DhcpStart["dhcp_start()"]
DhcpStart --> WaitBound{" 绑定成功? "}
WaitBound -->|"成功"| Succ["派发 LWIP_*_DHCP_BOUND_SUCC"]
WaitBound -->|"15s 超时 (100ms 轮询)"| Timeout{"IPV4_ADDR_CONFLICT_DETECT?"}
Timeout -->|"开启"| Detect["IP 冲突检测<br/>lwip_etharp_cleanup_netif"]
Timeout -->|"关闭"| Evt["派发 LWIP_*_DHCP_BOUND_TIMEOUT"]
Succ --> Up["netif 置 up,应用可建连"]
Detect --> Evt
Evt --> Retry["应用可重新 lwip_renew 或回退静态 IP"]
DHCP 失败后不会死等:15 秒超时即上报 BOUND_TIMEOUT,应用可自行决定重试、回退静态 IP 或提示用户——这是嵌入式联网"永不阻塞"设计原则的体现。
使用示例(Usage Examples)
示例 1:curl 在 lwIP 上的初始化
curl 通过 USE_LWIPSOCK 宏直接使用 lwIP 的 socket 实现,初始化时调用 lwip_init():
#elif defined(USE_LWIPSOCK)
lwip_init();
#endif
Source: easy.c
这印证了移植层的完整性:lwIP 的 tcpip 核心可直接被第三方库初始化,LwIP.c 的 Init_LwIP 是更上层的多 netif 封装,二者可独立使用。
示例 2:应用侧初始化与事件订阅(典型调用序列)
基于 lwip.h 导出 API 的典型用法(SDK 上层联网任务的标准模式):
#include "lwip.h"
/* 覆盖 weak 回调,感知网络状态 */
int lwip_event_cb(void *lwip_ctx, enum LWIP_EVENT event)
{
switch (event) {
case LWIP_WIRELESS_DHCP_BOUND_SUCC:
/* 已获得 IP,可以开始 socket 通信 */
break;
case LWIP_WIRELESS_DHCP_BOUND_TIMEOUT:
/* 超时,可重试 lwip_renew 或使用静态 IP */
break;
default:
break;
}
return 0;
}
/* 联网任务 */
void net_task(void)
{
Init_LwIP(WIFI_NETIF); /* 初始化协议栈与 Wi-Fi netif */
lwip_netif_set_up(WIFI_NETIF); /* 置 up 接口 */
lwip_renew(WIFI_NETIF, 1); /* 启动 DHCP */
lwip_set_default_netif(WIFI_NETIF); /* 设为默认路由接口 */
}
示例 3:查询接口配置
net_get_lan_info() 按接口编号返回配置表,net_get_netif_handle() 返回 netif 句柄,供上层读取 IP/网关:
struct lan_setting *net_get_lan_info(u8_t lwip_netif)
{
if (lwip_netif == WIFI_NETIF) {
return &wireless_lan_setting_info;
}
#ifdef HAVE_WRIELESS_RAW_NETIF
if (lwip_netif == WIFI_RAW_NETIF) {
return &wireless_raw_lan_setting_info;
}
#endif
#ifdef HAVE_EXT_WIRELESS_NETIF
else if (lwip_netif == EXT_WIFI_NETIF) {
return &ext_wireless_lan_setting_info;
}
#endif
/* ... LTE / ETH / BT 分支同理 ... */
return NULL;
}
Source: LwIP.c
设计意图:该查询函数把所有配置集中管理,上层拿到 lan_setting 后即可用 IP4_ADDR(&ip, ...) 组装 ip_addr_t,用于静态 IP 回退或 DHCP Server 配置,避免各模块各自维护 IP 常量。
配置选项(Configuration Options)
lwipopts.h 关键宏
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
LWIP_SOCKET | int | 1 | 启用 BSD Socket API(sockets.c) |
LWIP_COMPAT_SOCKETS | int | 1 | 提供 socket/connect 等标准名称的兼容层 |
LWIP_NETCONN | int | 0 | 关闭 Netconn API,省内存 |
LWIP_SO_RCVTIMEO / LWIP_SO_SNDTIMEO | int | 1 | 启用收发超时设置 |
LWIP_SO_SNDRCVTIMEO_NONSTANDARD | int | 1 | 超时参数用毫秒 int(winsock 风格)而非 timeval |
CONFIG_LWIP_IPV6_ENABLE | 宏 | 开启 | 支持 IPv6(含 DHCPv6) |
LWIP_HAVE_LOOPIF | int | 1 | 启用 loopback 接口 |
MULTI_NETIF_SUPPORT | int | 0 | 多 netif 高级路由支持(默认关) |
JL_LWIP | int | 1 | 杰理平台定制标志 |
LWIP_WND_SCALE / TCP_RCV_SCALE | int | 0 / 0 | 关闭 TCP 窗口缩放 |
ETHARP_SUPPORT_STATIC_ENTRIES | int | 1 | ARP 静态表项支持 |
DHCPS_UNICAST_ENABLE | int | 0 | DHCP Server 单播应答(默认关) |
DEFAULT_ACCEPTMBOX_SIZE | int | 60 | listen 连接积压邮箱容量 |
DEFAULT_RAW_RECVMBOX_SIZE | int | 61 | RAW socket 收包邮箱容量 |
LWIP_NETIF_HOSTNAME | int | 1 | netif 主机名支持(驱动中设置) |
LWIP_NETIF_REMOVE_CALLBACK | int | 1 | netif 移除回调(LTE 驱动使用) |
LwIP.c 运行期常量
| 常量 | 类型 | 默认值 | 说明 |
|---|---|---|---|
DHCP_TMR_INTERVAL | 宏 | 100 ms | DHCP 超时轮询周期 |
dhcp_timeout_msec | static u32 | 15000 ms | DHCP 绑定超时阈值 |
use_dhcp | static u8 | 1 | 以太网接口默认走 DHCP |
IPV4_ADDR_CONFLICT_DETECT | extern const u8 | 外部定义 | 是否开启 IPv4 地址冲突检测 |
API 参考(API Reference)
以下 API 由 lwip.h 导出、LwIP.c 实现,是上层与协议栈移植层的主要交互接口。
void Init_LwIP(u8_t lwip_netif)
初始化 lwIP 协议栈并注册指定网络接口。
- 参数:
lwip_netif—— 接口编号,取ETH_NETIF/WIFI_NETIF/BT_NETIF/LTE_NETIF/EXT_WIFI_NETIF/WIFI_RAW_NETIF。 - 说明:内部通过
static u8 lwip_inited保证lwip_init()(tcpip 核心初始化)全局只执行一次;随后为该接口执行netif_add()与默认配置装载(调用net_get_lan_info对应表)。 - 返回:无。重复调用同一接口需先
lwip_netif_set_down或走lwip_renew流程。
void lwip_netif_set_up(u8_t lwip_netif) / void lwip_netif_set_down(u8_t lwip_netif)
置 up / 置 down 指定接口(封装 lwIP 的 netif_set_up / netif_set_down)。
- 参数:
lwip_netif—— 接口编号。 - 返回:无。置 down 后该接口停止收发;置 up 后恢复,并触发链路状态变化。
void lwip_set_default_netif(u8_t lwip_netif)
将指定接口设为默认路由出口(netif_set_default)。
- 参数:
lwip_netif—— 接口编号。 - 说明:多接口共存时必须显式指定默认 netif,否则发往外部网络的报文无路由。
void lwip_renew(u8_t lwip_netif, u8_t dhcp)
为接口启动 DHCP(dhcp=1)或应用静态 IP 配置(dhcp=0)。
- 参数:
lwip_netif—— 接口编号;dhcp—— 1 表示dhcp_start()动态获取,0 表示用lan_setting静态配置(并维护lwip_static_ip_renew[]续租标志)。
- 返回:无。结果通过
LWIP_EVENT回调异步上报。 - 相关事件:
LWIP_*_DHCP_BOUND_SUCC/LWIP_*_DHCP_BOUND_TIMEOUT。
struct lan_setting *net_get_lan_info(u8_t lwip_netif)
获取指定接口的局域网静态配置表。
- 参数:
lwip_netif—— 接口编号。 - 返回:指向对应
struct lan_setting的指针;编号非法或对应HAVE_*_NETIF未编译时返回NULL。
struct netif *net_get_netif_handle(u8_t lwip_netif)
获取指定接口的 lwIP netif 句柄。
- 参数:
lwip_netif—— 接口编号。 - 返回:
struct netif *,可用于直接调用 lwIP 核心 API(如netif_ip4_addr()读取 IP)。
事件回调(weak,可覆盖)
int lwip_event_cb(void *lwip_ctx, enum LWIP_EVENT event); /* Wi-Fi / 通用 */
int lte_lwip_event_cb(void *lwip_ctx, enum LWIP_EVENT event); /* LTE */
int ext_lwip_event_cb(void *lwip_ctx, enum LWIP_EVENT event); /* 扩展无线 */
int eth_lwip_event_cb(void *lwip_ctx, enum LWIP_EVENT event); /* 有线以太网 */
int bt_lwip_event_cb(void *lwip_ctx, enum LWIP_EVENT event); /* 蓝牙 */
int dns_set_server(unsigned int *dnsserver); /* DNS 服务器注入 */
int socket_send_but_netif_busy_hook(int s, char type_udp); /* 忙时发送拦截 */
- 参数:
lwip_ctx—— 协议栈上下文(当前实现传 NULL);event——LWIP_EVENT枚举值。 - 返回:非 0 可被移植层视为已处理(默认实现返回 0)。
- 设计意图:weak 符号机制让"未覆盖也能链接、覆盖即生效",是嵌入式 C 中零开销的观察者模式。
失败模式、边界情况与并发(Failure Modes, Edge Cases & Concurrency)
DHCP 超时
- 默认 15 秒未绑定即触发
LWIP_*_DHCP_BOUND_TIMEOUT。上层必须处理该事件(重试 / 静态 IP 回退 / 提示用户),否则设备将长时间无 IP。 dhcp_timeout_msec与DHCP_TMR_INTERVAL均为移植层常量,可在不修改 lwIP 核心的前提下调整超时策略。- 超时路径会调用
lwip_etharp_cleanup_netif()清理 ARP 表,避免更换网络后旧邻居表项导致通信失败。
IP 冲突
IPV4_ADDR_CONFLICT_DETECT(外部定义,默认在协议栈事件层使能时)在静态 IP / DHCP 场景下做冲突检测,冲突时触发重新获取或上报,防止同一网段内地址重复。
多接口并发与默认路由
- 最多 6 个 netif 可同时存在,但
MULTI_NETIF_SUPPORT=0,即不启用 lwIP 的多路由表,同一时刻只有lwip_set_default_netif指定的接口承载对外路由;应用切换上网通道(如 Wi-Fi → LTE)时必须先set_down旧接口再set_up+set_default新接口,并重新lwip_renew。 - 各接口的 DHCP 超时计数器相互独立,事件回调也按接口拆分(
lte_lwip_event_cb等),避免跨接口事件串扰。
线程与重入
- lwIP 采用 tcpip 线程 + 邮箱模型,
sys_arch.h提供信号量/邮箱抽象;Socket 调用在应用线程执行,内部经tcpip_api_call或锁同步到 tcpip 线程。 lwip_init()由lwip_inited标志保护,多任务同时调用Init_LwIP时不会重复初始化核心(但接口注册仍需应用侧保证时序)。- weak 回调默认实现为纯函数(返回 0),应用覆盖时应注意回调运行在哪个上下文(DHCP 轮询定时器 / 事件线程),避免在回调中做阻塞操作或直接调用 lwIP 非线程安全接口。
资源边界
DEFAULT_ACCEPTMBOX_SIZE=60意味着 listen 积压队列最多约 60 个待 accept 连接,超出后新连接会被拒绝——高并发服务器应用需注意;LWIP_WND_SCALE=0时 TCP 窗口上限受限(典型 64KB),大带宽传输需评估;- 每连接 PCB/pbuf 内存来自 lwIP 内存池,连接数过多会触发
MEMP分配失败,表现为connect返回ENOMEM,应用应限制并发 socket 数。
性能与运维考量(Performance & Operational Notes)
- 内存优先的裁剪策略:
LWIP_NETCONN=0、窗口缩放关闭、LWIP_COMPAT_SOCKETS=1,在保证 BSD API 兼容性的同时把协议栈 RAM 占用压到最低,适配 AC79 这类资源受限的 SoC。 - DHCP 轮询开销:100ms 定时器只做计数与超时判断,不阻塞 tcpip 线程,开销可忽略。
- 默认接口切换是运维热点:切换上网通道时建议先
set_down再set_up,并调用lwip_etharp_cleanup_netif()(移植层已在相关路径处理),否则新链路的首包可能被旧 ARP 缓存拖累。 - 调试入口:
printf.h已在LwIP.c引入,可结合 lwIP 的LWIP_DEBUG系列宏(TCP_DEBUG、DHCP_DEBUG等)输出协议栈内部日志,快速定位丢包/超时问题。
扩展点(Extension Points)
- 新增网络接口:在
lwip.h的 netif 编号表追加宏(MAX_NETIF_NUM同步 +1),在LwIP.c中仿照HAVE_*_NETIF增加编译开关、lan_setting实例与net_get_lan_info分支,并实现xxx_ethernetif_init()驱动(参考bt_ethernetif.c/lte_ethernetif.c模板)。 - 自定义事件:在
enum LWIP_EVENT末尾追加枚举值,在移植层触发点调用对应 weak 回调即可,向后兼容既有事件处理代码。 - 协议栈功能裁剪:通过
lwipopts.h宏按产品需求开关(IPv6、Socket、窗口缩放、DHCP Server 单播等);另有lwipopts_bt.h、lwipopts_sfc.h变体可按产品线分别配置。 - 应用层协议模块:
include_lib/net/lwip_2_2_0/lwip/app/提供 NTP 客户端(ntp.h)、DHCP Server(dhcp_srv.h)、mSSDP(mssdp.h)及杰理私有协议 cdp/ctp/dtp,可直接复用;sock_api.h提供面向业务的任务化 Socket 封装。 - 驱动钩子:
socket_send_but_netif_busy_hook允许在 netif 忙时拦截发送,可用于背压控制或缓存策略。
相关链接(Related Links)
- 源码目录:lib/net/lwip_2_2_0 与 include_lib/net/lwip_2_2_0
- 移植层主文件:LwIP.c
- 移植层头文件:lwip.h
- 协议栈配置:lwipopts.h
- 应用协议模块:lwip/app/sock_api/sock_api.h、lwip/app/ntp/ntp.h、lwip/app/dhcp_srv/dhcp_srv.h
- curl 的 lwIP 集成:easy.c
- 相关同级页面:Wi-Fi 连接管理、蓝牙协议栈、LTE 模组、HTTP/curl 应用层(详见网络目录 5-networking 下其它条目)