@@ -3,8 +3,10 @@
> **定位**:本文档是 `docs/网络/BSD_Socket_API_使用指南.md`(下称"使用指南")的**内部视图**伴读文档。
> 使用指南回答"**上层该怎么调**"(公开语义 / 示例 / 常见问题);本文回答"**内部函数各自做什么、互相怎么调用、为什么这么设计**"(调用关系 / 设计意图 / 踩坑)。
>
> **范围**: MCU 端 `Drivers/BSP/NET/net_socket.c`(约 28 70 行,全部函数)。
> **范围**: MCU 端 `Drivers/BSP/NET/net_socket.c`(约 36 70 行) + 其拆出的
> `Drivers/BSP/NET/net_listener.c`(约 550 行,**监听生命周期**,N-32 §10 模块化)。
> 模拟器端后端 `simulator/sim/net_socket_sim.c`( Winsock)是另一套实现,见 `docs/网络/BSD_Socket_API_使用指南.md` §2.3; `net_select.c` 不在此展开,见 `docs/网络/BSD_Socket_API_使用指南.md` §7 / §10.5。
> `net_listener.c` **只被 `net_socket.c` include**(内部头,不进公开 `net_socket.h`),模拟器零依赖、不编译;其设计依据见 `docs/子系统设计/网络Socket监听生命周期设计说明.md` §10。
>
> **交叉引用约定**:机制标 Trap 号 → `docs/问题记录/CH395F_Trap_Records.md`;用法语义引用使用指南 §;关键参数值与 `net_config.h` / `net_socket.c` 内宏一致。
@@ -12,7 +14,7 @@
## 1. 一句话架构
**一切芯片(CH395F)操作收敛到唯一执行者 netTask 串行完成 ** ;其他任务通过"入队消息 + 任务通知"表达请求,结果只经**任务通知值**回传;阻塞等待**不独占 netTask**(挂起登记 + 主循环节拍完成);监听器**常驻**(`auto_relisten` + D10 非阻塞重试 + Trap 22 周期对账 + Trap 23 排渣隔离,四 层自愈)。
**一切芯片(CH395F)操作收敛到唯一执行者 netTask 串行完成 ** ;其他任务通过"入队消息 + 任务通知"表达请求,结果只经**任务通知值**回传;阻塞等待**不独占 netTask**(挂起登记 + 主循环节拍完成);监听器**常驻**(`auto_relisten` + D10 非阻塞重试 + Trap 22 周期对账 + Trap 23 排渣隔离 + **N-32 监听器 pump(唯一重建入口) ** ,五 层自愈)。
---
@@ -20,7 +22,7 @@
| 执行域 | 运行位置 / 节奏 | 允许的操作 | 典型调用者 |
|---|---|---|---|
| **netTask 主循环 ** | 独立 FreeRTOS 任务,1ms/tick | **唯一**可碰芯片(SPI2)的地方;3 个非阻塞 tick;队列分派;事件处理器;用户回调 | `net_poll` / `net_process_messages` 及其下游全部 `_locked` / `_tick` / `handle_*` |
| **netTask 主循环 ** | 独立 FreeRTOS 任务,1ms/tick | **唯一**可碰芯片(SPI2)的地方;4 个非阻塞 tick( D10 重试 / L3 挂起 / Trap 22 对账 / N-32 pump) ;队列分派;事件处理器;用户回调 | `net_poll` / `net_process_messages` 及其下游全部 `_locked` / `_tick` / `handle_*` |
| **应用任务侧 ** | ftpTask / uiTask / timeSyncTask / testTask 等 | 经队列的公共 API(入队 + `xTaskNotifyWait` 等回执);纯 RAM 直调 API;**禁止直碰芯片** | 各服务任务 |
| **直调快路径 ** | 同步调用,不经过队列 | `net_socket` / 查询族 / 回调注册(纯 RAM);**直碰 SPI 的三例外**: `net_bind` (UDP) / `net_sendto` / `net_recvfrom` | 初始化阶段、netTask 上下文、测试套件(硬件层测试模式下 netTask 自挂起,testTask 独占芯片) |
@@ -43,11 +45,12 @@ flowchart TB
XN["xTaskNotify / xTaskNotifyWait<br/>(结果回传的唯一通道)"]
subgraph NT["netTask 主循环 — 1ms/tick;芯片操作唯一执行者"]
NPM["netProcessMessages<br/>先跑 3 个 tick,再排空队列分派"]
NPM["netProcessMessages<br/>先跑 4 个 tick,再排空队列分派"]
NP["netPoll<br/>INT# GPIO 轮询 → 全局中断 → 各 Socket SINT → 每轮一批"]
T1["① netRel istenRetryTick<br/>D10 重监听重试:30ms / 2s 时限"]
T1["① netL istener RetryTick<br/>D10 重监听重试:30ms / 2s 时限"]
T2["② netPendingTick<br/>L3 挂起 recv:①数据 ②到点 ③断连 ④看门狗 1s"]
T3["③ netReconcileTick<br/>Trap 22 对账:200ms / 去抖 2 次 / 冷却 1s "]
T3["③ netReconcileTick<br/>Trap 22 对账:200ms / 去抖 / 冷却(只剩①②) "]
T4["④ netListenerPump<br/>N-32 唯一重建入口:100ms / 去抖 / 冷却"]
DISP["队列分派 switch<br/>SEND · RECV · CLOSE · CONNECT · LISTEN<br/>ACCEPT · RESYNC_LISTEN · RELISTEN"]
subgraph LK["_locked / 内部分派目标(仅 netTask 上下文)"]
LL["netListenLocked<br/>初次监听(3× 500ms 阻塞重试)"]
@@ -56,10 +59,11 @@ flowchart TB
LR["netRecvLocked<br/>快路径 / DONTWAIT / 登记挂起"]
LA["netAcceptLocked<br/>先 netPoll + 状态判定"]
LC["netCloseLocked<br/>了结挂起 + close + 等 CLOSED + 排空中断"]
RLS["netRel istenTry → netRelistenLocked<br/>单次尝试 + Trap 23 排渣隔离"]
RLS["netL istener Try → netRelistenLocked<br/>单次尝试 + Trap 23 排渣隔离"]
RSY["RESYNC 分支:改状态 + 取消挂起<br/>(不发芯片命令)"]
end
EV["事件处理器<br/>handleConnect · handleDisconnect<br/>handleRecv · handleSend · handleTimeout"]
ST["netListenerSettle<br/>只置沉降/隔离事实(100ms / 400ms) "]
FE["fireEvent → 用户回调"]
PR["s_net_pending<br/>(每 fd 至多一个挂起)"]
CH[("ch395f.* 驱动<br/>SPI2 串行事务")]
@@ -68,16 +72,16 @@ flowchart TB
PUB -->|入队| MQ
MQ --> DISP
DISP --> LL & LN & LS & LR & LA & LC & RLS & RSY
NPM --> T1 & T2 & T3 & NP
NPM --> T1 & T2 & T3 & T4 & NP
T1 --> RLS
T3 -->|"① 已断"| EV
T3 -->|"② 补 accept"| EV
T3 -->|"③ 重建监听 "| RLS
T4 -->|"①② 芯片失位即重建 "| RLS
NP --> EV
EV --> FE
FE -.-> CB
EV -->|"LISTENING:监听丢失"| RLS
EV -->|"TIMEOUT:置隔离"| RLS
EV -->|"断连/超时:只置事实"| ST
ST -. ->|"事实 → 由 pump 统一重建"| T4
LR -->|无数据+阻塞| PR
PR --> T2
T2 -->|xTaskNotify| XN
@@ -91,9 +95,10 @@ flowchart TB
**读图要点 ** :
1. 应用侧只有两种"等":① 入队后 `xTaskNotifyWait` 等回执(同步语义);② 挂起 recv 的等待(同一通道,只是"完成"由 `netPendingTick` 判定,不由 netTask 立即处理)。
2. `net_process_messages()` 开头的 **3 个 tick 是网络层自愈主干 ** :没有任何应用消息时,监听恢复 / 挂起完成 / 状态对账照常运转——"断连后即使没人发消息,监听器也能自己活过来"靠的就是它们。
3. 事件处理器**只由 `net_poll` 驱动**,例外是 `net_reconcile_tick` (带 2 次去抖 + 1s 冷却)可再入 `handle_disconnect_event` / `handle_connect_event` / `net_re listen_try` ——对账是丢中断的唯一自愈通道(Trap 22) 。
2. `net_process_messages()` 开头的 **4 个 tick 是网络层自愈主干 ** :没有任何应用消息时,重试 / 挂起完成 / 状态对账 / 监听重建 照常运转——"断连后即使没人发消息,监听器也能自己活过来"靠的就是它们。
3. 事件处理器**只由 `net_poll` 驱动**,例外是 `net_reconcile_tick` (带 2 次去抖 + 1s 冷却)可再入 `handle_disconnect_event` / `handle_connect_event` ——对账是丢中断的自愈通道(Trap 22)。**注意职责边界(N-32 P2)**:对账与事件处理器**只置事实**(置 CLOSED / 登记沉降),**重建监听的动作统一由 `net_listener_pump` 做**(本层唯一重建入口)——"谁先看到 DISCONNECT 就谁重建"的竞争在结构上消失 。
4. 回调 `event_cb` 在 **netTask 上下文**执行:回调内不得阻塞、不得发网络操作。
5. **模块归属 ** : `T1` ( D10 重试)/ `T4` ( pump) / `RLS` 的 `net_listener_try` / `ST` ( settle)在 `net_listener.c` (真机专属、模拟器不编译);其余 tick 与 `_locked` 在 `net_socket.c` 。
---
@@ -112,7 +117,7 @@ flowchart TB
| 函数 | 设计作用 | 主要调用 | 意图 / 注意 |
|---|---|---|---|
| `net_poll()` | 事件泵:GPIO 轮询 INT#(低=有待处理)→ `GET_GLOB_INT_STATUS_ALL` (读=清)→ 按位图逐 Socket 读 SINT → 分派 5 类事件 | `HAL_GPIO_ReadPin` → `ch395f_get_glob_int_status_all` → `ch395f_get_sock_int_status` × n → `handle_connect/disconnect/recv/send/timeout_event` | **RECV 中断是电平触发 ** :每次调用**只处理一批**(do-while(0)),处理完立即返回,否则主循环(echo/应用逻辑)饿死(Trap 08);SENBUF_FREE 只置 `send_ready` 不记事件 |
| `net_process_messages()` | 队列入口 + 自愈 tick 宿主:每轮**先**跑 3 个非阻塞 tick,再排空队列逐条分派,结果 `xTaskNotify` 回传 | `net_re listen_retry_tick` → `net_pending_tick` → `net_reconcile_tick` → `osMessageQueueGet` (0 超时) × n → switch 分派 → `xTaskNotify` | ① tick 放在队列**之前**:断连后即使没有任何应用消息,监听器也能自主恢复、挂起 recv 也能完成;② RECV 若返回 `NET_PENDING_REGISTERED` (哨兵 0x70000000)则**不发即时回执**( `do_notify=0` ),完成时才通知;③ 失败结果把 `s_net_errno` (具体负错误码)压入通知值——调用方恢复 errno 后对外统一 -1(BSD 语义);④ 通知值**整个 32 位**都是结果(不压位域标签,否则 32768 这类大字节数会溢出为负) |
| `net_process_messages()` | 队列入口 + 自愈 tick 宿主:每轮**先**跑 4 个非阻塞 tick,再排空队列逐条分派,结果 `xTaskNotify` 回传 | `net_listener _retry_tick` → `net_pending_tick` → `net_reconcile_tick` → `net_listener_pump` → `osMessageQueueGet` (0 超时) × n → switch 分派 → `xTaskNotify` | ① tick 放在队列**之前**:断连后即使没有任何应用消息,监听器也能自主恢复、挂起 recv 也能完成;② RECV 若返回 `NET_PENDING_REGISTERED` (哨兵 0x70000000)则**不发即时回执**( `do_notify=0` ),完成时才通知;③ 失败结果把 `s_net_errno` (具体负错误码)压入通知值——调用方恢复 errno 后对外统一 -1(BSD 语义);④ 通知值**整个 32 位**都是结果(不压位域标签,否则 32768 这类大字节数会溢出为负);⑤ **pump 置于对账之后 ** :对账①可能刚把会话置 CLOSED,pump 紧接着就能看到并重建 |
### 4.3 经队列的公共 API 及其 netTask 侧实现
@@ -121,20 +126,20 @@ flowchart TB
| 公共 API | 消息 | netTask 侧实现 | 关键芯片命令 | 返回语义 | 设计意图 / 注意 |
|---|---|---|---|---|---|
| `net_listen` | `NET_MSG_LISTEN` | `net_listen_locked` | `set_proto_type` + `set_sour_port` + `open_socket` ( **3× 500ms 阻塞重试**) + `tcp_listen` + 清全部中断状态 | 0 / -1 | **初次**监听允许阻塞重试(启动期无竞争);成功后置 `auto_relisten=1` ——监听常驻的源头;启动前清空各 Socket/全局中断状态,防历史残留误触发事件 |
| `net_accept` | `NET_MSG_ACCEPT` | `net_accept_locked` | 无(先 `net_poll` 泵一次 + 状态判定) | 监听 fd 自身(单连接)/ -1( WOULDBLOCK) | 单连接:监听 Socket 在 CONNECT 后**自身即数据通道**,返回的就是它;CLOSED + auto_relisten 时触发**非阻塞** `net_re listen_try` ; **accept 路径不做自愈**——单次状态读会误判"刚 listen 未生效"致重建风暴(2026-09-23 实测),统一交给对账 |
| `net_accept` | `NET_MSG_ACCEPT` | `net_accept_locked` | 无(先 `net_poll` 泵一次 + 状态判定) | 监听 fd 自身(单连接)/ -1( WOULDBLOCK) | 单连接:监听 Socket 在 CONNECT 后**自身即数据通道**,返回的就是它;CLOSED + auto_relisten 时触发**非阻塞** `net_listener _try` ; **accept 路径不做自愈**——单次状态读会误判"刚 listen 未生效"致重建风暴(2026-09-23 实测),统一交给 `net_listener_pump` ( N-32 单一重建入口) |
| `net_connect` | `NET_MSG_CONNECT` | `net_connect_locked` | `set_proto_type` + `open_socket` (3×500ms 重试)+ 清残留中断 + `set_sour_port` / `set_des_ip` (大端数组)/ `set_des_port` + `tcp_connect` | 0 = 请求已发出(**异步**) / -1 | open **先于**寄存器写入(open 会复位寄存器);open 后清残留 DISCONNECT,防新连接秒断(Trap 14/17);目的 IP 字节序写反 = CONNECT TIMEOUT( Trap 16);返回 0 ≠ 已建立——调用方以 `net_sock_state` 轮询等 ESTABLISHED |
| `net_send` | `NET_MSG_SEND` | `net_send_locked` | `ch395f_write_send_buf` (分块)+ 等 SENBUF_FREE( `net_poll` 轮询) | 实际字节数 / -1 | 分块 ≤ `NET_SEND_CHUNK_MAX` = 1KB( **硬件发送 FIFO 硬上限**, Trap 20:写穿 FIFO = 数据错位);`send_ready` 门控用 `net_poll` 轮询而非 osDelay;短写**必须**告警——静默截断会让对端永久等待剩余字节(2026-09-21 实证:UI 推流应答截断 → 客户端失联) |
| `net_recv` | `NET_MSG_RECV` ( timeout=0→默认 30s) | `net_recv_locked` | `net_poll` + `get_recv_len` / `read_recv_buf` | >0 / 0(EOF) / -1( errno = WOULDBLOCK / TIMEDOUT / CONNRESET) | 快路径:数据已在 → 立即读出返回(允许部分读);无数据 + 阻塞 → **登记挂起后立即返回 ** ( L3, netTask 零占用);DONTWAIT + CLOSED → EOF + 触发重监听 |
| `net_recv_to` | `NET_MSG_RECV` ( timeout>0) | `net_recv_locked` | 同上 | >0 / 0 / -1 / * * -2(本片超时无数据)** | E-02 头阻塞修复配套:调用方把长等待切成有界片(如 5ms/剩余预算),**总超时语义不变、单次占用有界**;`timeout_ms=0` 直接拒(会被解释成默认 30s,恰是长阻塞);超时**只凭 -2 判定、不得依赖 errno**(`s_net_errno` 是跨任务共享全局量,随时被别的任务覆盖) |
| `net_close` | `NET_MSG_CLOSE` | `net_close_locked` | `close_socket` + 等 CLOSED( 5ms 节奏 / ≤500ms) + 排空 Socket/全局中断 | 0 / -1 | **先**了结本 fd 挂起 recv(排空/取消 + 必通知,关不得让等待者悬挂);关后排空残留 DISCONNECT( Trap 14/17:否则下一连接被"刚建立就断开"秒断);控制块整体清零 |
| `net_relisten` | `NET_MSG_RELISTEN` | `net_re listen_try` → `net_relisten_locked` | `set_proto_type` + `set_sour_port` + `open_socket` + `tcp_listen` (单次尝试;隔离 期另有 `close` + `get_socket_status` + 排空) | 0 / -1 | 无条件芯片侧重建:芯片侧已在监听 → 首次 open 失败 → close 重建(短暂中断 ~1s);失败**不阻塞**,登记 D10 重试;用途 = 代次变化后显式重建 / 判死后确定性重建;**-1 = "恢复中"**(重试轮询接管),不是永久失败 |
| `net_relisten` | `NET_MSG_RELISTEN` | `net_listener _try` → `net_relisten_locked` | `set_proto_type` + `set_sour_port` + `open_socket` + `tcp_listen` (单次尝试;沉降 期另有 `close` + `get_socket_status` + 排空) | 0 / -1 | 无条件芯片侧重建:芯片侧已在监听 → 首次 open 失败 → close 重建(短暂中断 ~1s);失败**不阻塞**,登记 D10 重试;用途 = 代次变化后显式重建 / 判死后确定性重建;**-1 = "恢复中"**(重试轮询接管),不是永久失败 |
| `net_resync_listen` | `NET_MSG_RESYNC_LISTEN` | 分派内直接处理(**无芯片命令**) | 无 | 0 / -1 | D14 静默回收场景:芯片已自行把 Socket 回收回监听且**不发 DISCONNECT 中断** → 本层滞留 ESTABLISHED。只修 MCU 侧(state → LISTENING + 取消该 fd 挂起 recv);**严禁**发芯片 close/重监听——会摧毁芯片侧健康监听器 → 连接永拒(run8 实证 170s 拒绝)。非 ESTABLISHED 时视为已一致,返回 0 |
### 4.4 直调 API(不经队列)
| 函数 | 设计作用 | 主要调用 | 意图 / 注意 |
|---|---|---|---|
| `net_socket(domain,type)` | 分配空闲控制块(`alloc_socket` 线性扫描 `in_use=0` )→ CREATED | `alloc_socket` + `net_re listen_clear` + `net_pending_clear` | 新 fd 不得继承上一次的待重试/挂起状态(复用描述符的经典坑);domain 仅 `NET_AF_INET` |
| `net_socket(domain,type)` | 分配空闲控制块(`alloc_socket` 线性扫描 `in_use=0` )→ CREATED | `alloc_socket` + `net_listener _clear` + `net_pending_clear` | 新 fd 不得继承上一次的待重试/挂起状态(复用描述符的经典坑);domain 仅 `NET_AF_INET` |
| `net_bind(sockfd,addr,addrlen)` | TCP:仅记录本地端口(port=0 → 动态端口 49152 起递增)→ BOUND,**无芯片命令**; UDP:完整开 Socket → UDP_OPEN | UDP 路径:`set_proto_type(UDP)` + `set_sour_port` + `set_des_ip(0xFFFFFFFF)` + `set_des_port(0)` + `open_socket` ( Socket 4~7 兜底重设缓冲) | TCP 的 bind 只是"记账",真正的芯片动作在 listen/connect; UDP 直碰 SPI(§2 三例外之一);动态端口回绕 65535→49152 |
| `net_sendto` / `net_recvfrom` | UDP 收发(直碰 SPI,不经队列) | `set_des_ip` / `set_des_port` + `write_send_buf` (分块 1KB + `net_poll` 门控);`net_poll` + `get_recv_len` + `read_recv_buf` | 同 §2 上下文约束;recv 时 UDP Server 模式前 8 字节是固定头(源 IP + 端口 + 长度),先剥头填 `src_addr` 再读数据 |
| `net_wait_result(kind,sockfd)` | 调用侧两段式等待:**零等待拾取** → 阻塞等回执 | `xTaskNotifyWait` ×2 | ① 零等待段兜住"入队→等待入口"间隙落地的通知(2026-09-22 台架实证丢通知);② **清除掩码必须全 1 ** (传 0 = 不清 → 上次结果残留被重读,run3~11 挂死/错位的真根因);③ 必须判 **pdTRUE ** 而非 `v!=0` (合法结果 0 = EOF 会被误判"无通知"→ 永久挂死);④ 32 位整体作结果(32768 大字节数不截断);⑤ 本路径**禁止**加逐调用日志(曾占日志 98.6%、冲掉 [LAT] 证据行,run14 实证) |
@@ -152,53 +157,77 @@ flowchart TB
| 函数 | 设计作用 | 主要调用 | 意图 / 注意 |
|---|---|---|---|
| `net_pending_register` | 登记挂起(`net_recv_locked` 的"无数据 + 阻塞"路径) | 写 `s_net_pending[fd]` | 每 fd 至多一个:**覆盖会丢失前调用方通知 = 永久挂死**;执行任务自身(netTask)发起的等待直接拒绝(自等自通知 = 死锁,netTask 内须用 DONTWAIT) |
| `net_pending_tick` | 完成检查(`net_process_messages` 每轮调用,非阻塞) | ① `get_recv_len` + `read_recv_buf` ② 到点 ③ `get_recv_len` 排空 + `net_re listen_try` ④ `net_pending_complete` | 按 * * ①数据 ②到点 ③断连** 顺序检查(逐项对齐旧自旋语义);CONNECTING/LISTENING 等状态**保持等待**( CONNECT→ESTABLISHED 是合法迁移,不做状态推断取消,防误杀正在建链的等待);**看门狗**:到点后 1s 仍未了结 = 完成逻辑缺位 → 强制了结 + 错误日志 |
| `net_pending_tick` | 完成检查(`net_process_messages` 每轮调用,非阻塞) | ① `get_recv_len` + `read_recv_buf` ② 到点 ③ `get_recv_len` 排空 + `net_listener _try` ④ `net_pending_complete` | 按 * * ①数据 ②到点 ③断连** 顺序检查(逐项对齐旧自旋语义);CONNECTING/LISTENING 等状态**保持等待**( CONNECT→ESTABLISHED 是合法迁移,不做状态推断取消,防误杀正在建链的等待);**看门狗**:到点后 1s 仍未了结 = 完成逻辑缺位 → 强制了结 + 错误日志 |
| `net_pending_complete` | 所有完成/取消路径的公共出口 | `xTaskNotify(caller, result)` + 清 active | 通知值 = 结果原值(>0 字节 / 0 EOF / 负错误码,与消息分派同一回传通道);失败结果同步 `s_net_errno` (netTask 上下文一致性);无在位挂起时静默返回(防重复通知) |
| `net_pending_settle_close` | close 前**了结**本 fd 挂起 | `get_recv_len` + `read_recv_buf` + `net_pending_complete` | 关闭不得丢已到达数据(芯片缓冲 close 前可读):有数据 → 按字节数通知;无数据 → 取消(CONNRESET);先于 `ch395f_close_socket` 执行 |
| `net_pending_cancel` / `cancel_all` | 显式取消(**必通知**) | `net_pending_complete` | 取消路径:陈旧 resync(单 fd)/ 网络重建(全部,`net_init` 开头,早于控制块清零)——"取消不通知 = 调用方永久挂死" |
| `net_pending_clear` | 清除**不通知** | 清 `active` | 仅用于"新 fd 不得继承"( `net_socket` )与 close 后幂等清理(`net_close_locked` )——这两处前驱通知已由 settle 路径发出 |
### 4.6 D10 非阻塞重监听重试子系统(监听器自愈 )
### 4.6 D10 非阻塞重监听重试子系统(`net_listener.c` )
**模型 ** :重开失败 = **登记待重试 ** ( 30ms 节奏 / 2s 时限),`net_re listen_retry_tick` 到点重试——任何时刻不阻塞任务。旧实现 `osDelay(500)` 忙等:netTask 冻结 500ms(全网停摆)、实测恢复 588ms、期间重连被拒(M3 台架)。
**模型 ** :重开失败 = **登记待重试 ** ( 30ms 节奏 / 2s 时限),`net_listener _retry_tick` 到点重试——任何时刻不阻塞任务。旧实现 `osDelay(500)` 忙等:netTask 冻结 500ms(全网停摆)、实测恢复 588ms、期间重连被拒(M3 台架)。
> **N-32 §10( 2026-10-12) **:本子系统的函数已从 `net_socket.c` **抽出到 `net_listener.c`**(真机专属,模拟器不编译);并新增 `net_listener_settle` 取代外部直接写 `s_relisten_quarantine[]`(沉降等级化)。
| 函数 | 设计作用 | 主要调用 | 意图 / 注意 |
|---|---|---|---|
| `net_re listen_try` | 尝试一次;失败登记重试(**不阻塞 **) | `net _relisten_locked` → 成功 `net_relisten_done` / 失败置 `s_relisten_wait/next/dead` | 已在重试队列 → 直接返回 -1(**不重置重试时刻**):否则 10ms 一次的 accept 轮询会不断把重试时刻往后推 → 永不重试(饥饿) |
| `net_re listen_locked` | 真正的 open + listen( **单次尝试 **) | (隔离期) `net_drain_chip_rx` + `close_socket` + `get_sock_int_status` / `get_glob_int_status_all` + `get_socket_status ` → `s et_proto_type` + `set_sour_port` + `open_socket` + `tcp_listen` + 清中断 | ① **严禁 osDelay 忙等 ** :失败只清理并返回,沉降交给重试轮询;② **Trap 23 隔离门 ** :带隔离标记的 socket(异常释放 TIMEOUT,如中止上传)→ 持续排空 RX 残渣 + 主动 close + 排空中断 + 等芯片回 CLOSED/TCP_CLOSED,隔离满 400ms 才放行;**800ms 上限强制放行**(必须 < 2s 重试时限,否则隔离耗尽重试预算 → `relisten give up` 监听被弃——2026-09-23 实测修正);③ 成功只置 state=LISTENING, **不 memset**( fd 与 `auto_relisten` 保留——BSD 风格监听常驻 ) |
| `net_relisten_retry_tick` | 每轮到点重试 | `net_relisten_locked ` → `n et_relisten_done` / 顺延 / 放弃 | **同 fd 有在途挂起 recv 时跳过 ** (重开 Socket 会摧毁在途连接,L3 保险);到 2s 时限 → 清状态 + `relisten give up` 错误日志(应用可见 ) |
| `net_re listen_done` | 标记完成 | `net_relisten_clear` +(探针构建)恢复计时样本 | 恢复时延 = 首次尝试 → 完成(L3 探针 `relisten` 段 ) |
| `net_relisten_clear` | 取消该 fd 的待重试 | 清 wait/next/dead + **沉降/隔离状态 ** | 触发点: `net_socket` (新 fd 不继承)/ `net_close_locked` (关闭即取消);隔离状态必须一并清,否则残留标记会污染下一次复监听 |
| `net_listener_settle` | 登记"本 fd 会话已结束,重建前需沉降/隔离"(**只置事实 **) | 写 `s _relisten_quarantine[fd]` (等级:GRACEFUL 100ms / QUARANTINE 400ms) | N-32 P1:取代此前外部直接写 `s_relisten_quarantine[]` ;由 `handle_disconnect_event` ( GRACEFUL) / `handle_timeout_event` ( QUARANTINE)调用。**只置事实,动作交 pump/try**;越界 fd 静默忽略 |
| `net_listener_try` | 尝试重建一次;失败登记重试(**不阻塞 **) | `net_relisten_locked ` → 成功 `n et_relisten_done` / 失败置 `s_relisten_wait/next/dead` | 已在重试队列 → 直接返回 -1(**不重置重试时刻**):否则 10ms 一次的 accept 轮询会不断把重试时刻往后推 → 永不重试(饥饿 ) |
| `net_relisten_locked` | 真正的 open + listen( **单次尝试**) | (沉降期) `net_drain_chip_rx` + `close_socket` + `get_sock_int_status` / `get_glob_int_status_all` + `get_socket_status ` → `s et_proto_type` + `set_sour_port` + `open_socket` + `tcp_listen` + 清中断 | ① **严禁 osDelay 忙等 ** :失败只清理并返回,沉降交给重试轮询;② **Trap 23 沉降门 ** :带沉降标记的 socket(异常释放 TIMEOUT → QUARANTINE 400ms;正常释放 DISCONNECT → GRACEFUL 100ms)→ 排空 RX 残渣 + 主动 close + 排空中断 + 等芯片回 CLOSED/TCP_CLOSED; **上限强制放行**(必须 < 2s 重试时限,否则沉降耗尽重试预算 → `relisten give up` 监听被弃——2026-09-23 实测修正);③ 成功只置 state=LISTENING, **不 memset**( fd 与 `auto_relisten` 保留——BSD 风格监听常驻 ) |
| `net_listener_retry_tick` | 每轮到点重试(D10) | `net_relisten_locked` → `net_relisten_done` / 顺延 / 放弃 | **同 fd 有在途挂起 recv 时跳过 ** (重开 Socket 会摧毁在途连接,L3 保险);到 2s 时限 → 清状态 + `relisten give up` 错误日志(应用可见 ) |
| `net_relisten_done` | 标记完成 | `net_listener_clear` +(探针构建)恢复计时样本 | 恢复时延 = 首次尝试 → 完成(L3 探针 `relisten` 段) |
| `net_listener_clear` | 取消该 fd 的待重试/沉降 | 清 `wait/next/dead` + `settle_dead/settle_start` + `quarantine` | 触发点:`net_socket` (新 fd 不继承)/ `net_close_locked` (关闭即取消);沉降/隔离状态必须一并清,否则残留标记会污染下一次复监听 |
| `net_drain_chip_rx` | 排空芯片 RX 残留(256B 分块,≤16 次 = 4KB 上限) | `get_recv_len` + `read_recv_buf` | Trap 23:中止/异常断开时芯片 RX 缓冲残留上一条连接的数据(实测 ~2048B),不排空会被下一条连接当自己的数据读走(静默截断);分块读避免新增静态缓冲(内存预算紧张) |
### 4.7 Trap 22 周期对账(丢中断自愈 )
### 4.7 监听器 pump( N-32 P2 单一重建所有者,`net_listener.c` )
`net_listener_pump()` ( `net_process_messages` 每轮调用;周期 **100ms ** ):
**设计意图(设计文档 §4 不变量 II) ** :其它所有位置**只置事实**(会话结束 / 状态迁移 / 事件派发),**不做重建动作**——于是"谁先看到 DISCONNECT 就谁重建"这类**多所有者竞争在结构上消失**。本函数是**本层唯一**的周期性重建决策者。
**覆盖三种情形 ** (认领门槛:在用 + `auto_relisten` + TCP + 已绑端口):
| # | 本层状态 / 芯片状态 | 动作 | 来源 |
|---|---|---|---|
| ① | 本层 LISTENING 而芯片监听**已失位** | 重建(`net_listener_try` ) | 原对账③ |
| ② | 本层 CLOSED(会话已结束)而芯片**不在** LISTEN | 重建(`net_listener_try` ) | 原对账④ |
| ③ | 本层 CLOSED 而芯片**正在** LISTEN | **只做状态重同步 ** ( CLOSED→LISTENING),**不发任何芯片命令** | U11 缺口 |
情形③的理由:芯片监听器是**健康**的,重下发 open+listen 会因 `USE` 错误走进关闭分支、把正听着的 Socket 关掉(U12 同源事故);只把本层状态收回 LISTENING 即可(与 `net_resync_listen` 同一纪律)。
**去抖 + 冷却 ** (沿用原对账③④ 实测结论,取值不变):同一失配需**连续 2 次**(≈200ms)才动手;同一 Socket 动手后**冷却 1s**(防抖成风暴,2026-09-23 实证)。**周期取 100ms**(对账 200ms 的 1/2):比它快、又不至于像 50ms 那样频繁 close/open 与消费者收发撞车(2026-10-12 台架实测 50ms 会把 uiTask 最坏单轮从 172ms 推到 1034ms)。
> `session_dead` **不在此清零**(与 `net_relisten_locked` 同一契约:重建监听 ≠ 应用已确认会话结束;清零只发生在 `handle_connect_event`)。
### 4.8 Trap 22 周期对账(丢中断自愈,`net_socket.c`)
`net_reconcile_tick()` ( `net_process_messages` 每轮调用;周期 200ms):
**问题 ** :芯片在快速复连场景会**丢 CONNECT/DISCONNECT 中断**(或静默回收 Socket 不发中断);丢事件后纯事件驱动无法自愈——典型:对端连接一直占着单连接监听的"后备"(芯片 TCP 仍 ESTABLISHED),后续 SYN 全被拒,而应用因未收到 DISCONNECT 仍以为有客户端(永不调 accept)。
**机制 ** :对每个在用 Socket( ESTABLISHED / LISTENING)回读 `GET_SOCKET_STATUS` ,判定三类 失同步并修正:
**机制 ** :对每个在用 Socket( **仅 ESTABLISHED / LISTENING** )回读 `GET_SOCKET_STATUS` ,判定**两类** 失同步并修正:
| # | 失同步 | 动作 | 复用的实现 |
|---|---|---|---|
| ① | 本层 ESTABLISHED 而芯片已无连接(sock/tcp = CLOSED) | 按断开处理 → CLOSED + 回调(应用读到 EOF 后自动重 监听) | `handle_disconnect_event` (内置陈旧事件甄别) |
| ① | 本层 ESTABLISHED 而芯片已无连接(sock/tcp = CLOSED) | 按断开处理 → CLOSED + 回调(应用读到 EOF 后由 pump 重建 监听) | `handle_disconnect_event` (内置陈旧事件甄别) |
| ② | 本层 LISTENING 而芯片已完成握手(tcp = ESTABLISHED) | 补做 standalone accept 簿记 | `handle_connect_event` |
| ③ | 本层 LISTENING 而芯片监听已消失(异常关闭) | 重建监听 | `net_relisten_try` ( D10 非阻塞重试) |
> ⚠ **N-32 P2( 2026-10-12) **:原③(本层 LISTENING 丢监听)与④(孤儿监听)已**整体移交 `net_listener_pump`**——本函数内**不再有任何重建动作**(单一所有者),认领门槛相应收窄为 ESTABLISHED/LISTENING(原为驱动④而多认领"CLOSED + auto_relisten + 已绑端口")。
**去抖 + 冷却(缺一不可) ** :单次状态读可能落在"刚 open/listen 尚未生效"的瞬态——**2026-09-23 实测**:无去抖的 accept 路径自愈把监听反复重建直至 `relisten give up` (三服务口全死、89 行 ERROR)。故:同一失配需**连续 2 次**(≈400ms)才动手;同一 Socket 动手后**冷却 1s**。开销:每 200ms 每在用 Socket 一条状态命令,可忽略。
### 4.8 事件处理器(netTask 上下文;由 `net_poll` 驱动,对账 tick 可再入)
### 4.9 事件处理器(netTask 上下文;由 `net_poll` 驱动,对账 tick 可再入)
| 函数 | 设计作用 | 主要调用 | 意图 / 注意 |
|---|---|---|---|
| `handle_connect_event` | LISTENING → standalone accept(回读对端地址 → ESTABLISHED + `send_ready=1` + 清残留 TIMEOUT + 回调);CONNECTING → 客户端连接成功(回读对端 + 使能 KeepAlive + 回调) | `ch395f_get_remot_ipp` + `get_sock_int_status` +( client) `set_keepalive_enable` + `fire_event` | GET_REMOT_IPP 为**小端**输出(与 set_des_ip 的大端相反)——解析别抄反 |
| `handle_disconnect_event` | 状态机迁移 + 陈旧事件甄别 + 监听丢失 重建 | `ch395f_get_socket_status` →(LISTENING:芯片已不可监听 → `net_re listen_try` ,仍在监听 → 判陈旧丢弃)/(ESTABLISHED:芯片仍 ESTABLISHED → 判陈旧丢弃) | ① **陈旧 DISCONNECT 甄别 ** ( D20):同一 fd"关闭→重监听→再连接"循环复用,上一条连接的 FIN/ACK 可能在 fd 已复用时才上报 → 不甄别则新连接秒断(实测:FTP 同会话第二个数据连接必 425);② LISTENING 分支**已不可监听即重建**(Trap 22 修复的一半 );③ 置 CLOSED 后**不立即关芯片 Socket**——留给 `net_recv_locked` 排空剩余数据(FIN 可能先于数据到达) |
| `handle_connect_event` | LISTENING → standalone accept(回读对端地址 → ESTABLISHED + `send_ready=1` + 清残留 TIMEOUT + 回调);CONNECTING → 客户端连接成功(回读对端 + 使能 KeepAlive + 回调) | `ch395f_get_remot_ipp` + `get_sock_int_status` +( client) `set_keepalive_enable` + `fire_event` | GET_REMOT_IPP 为**小端**输出(与 set_des_ip 的大端相反)——解析别抄反; `session_dead` 的**唯一清零点** |
| `handle_disconnect_event` | 状态机迁移 + 陈旧事件甄别 + **登记沉降事实 ** (不再自己 重建) | `ch395f_get_socket_status` →(LISTENING:芯片已不可监听 → * * `net_listener_settle(GRACEFUL)` ** ,仍在监听 → 判陈旧丢弃)/(ESTABLISHED:芯片仍 ESTABLISHED → 判陈旧丢弃) | ① **陈旧 DISCONNECT 甄别 ** ( D20):同一 fd"关闭→重监听→再连接"循环复用,上一条连接的 FIN/ACK 可能在 fd 已复用时才上报 → 不甄别则新连接秒断(实测:FTP 同会话第二个数据连接必 425);② 正常释放(FIN)→ **GRACEFUL 沉降 100ms ** (只置事实,重建由 `net_listener_pump` 做,N-32 P1/P2 );③ 置 CLOSED 后**不立即关芯片 Socket**——留给 `net_recv_locked` 排空剩余数据(FIN 可能先于数据到达) |
| `handle_recv_event` | 置 `readable` + 回调;**不读 recv_len** | `fire_event` | 不读长度是为了避免与 `net_recv_locked` 的 `ch395f_get_recv_len` 竞争(Trap 13:分批读会归零);`readable` 供 `net_select` 无 SPI 访问判读 |
| `handle_send_event` | 发送缓冲已空:触发回调(`send_ready` 由 SENBUF_FREE 前置位) | `fire_event` | — |
| `handle_timeout_event` | CONNECTING → CREATED(连接超时);**ESTABLISHED → 置重监听 隔离 + CLOSED**( Trap 23);LISTENING → 仅记日志 | (隔离)写 `s_relisten_quarantine[fd]` + `fire_event` | TIMEOUT = 异常释放(如客户端中止上传):对端在途数据随后落进 RX 缓冲 → 必须隔离后才许复监听;正常 FIN 释放走 disconnect 路径,**不走**隔离(零改动) |
| `handle_timeout_event` | CONNECTING → CREATED(连接超时);**ESTABLISHED → 登记 隔离 + CLOSED**( Trap 23);LISTENING → 仅记日志 | * * `net_listener_settle(fd, NET_LISTENER_SETTLE_QUARANTINE)` ** + `fire_event` | TIMEOUT = 异常释放(如客户端中止上传):对端在途数据随后落进 RX 缓冲 → 必须隔离( 400ms) 后才许复监听;正常 FIN 释放走 disconnect 路径( GRACEFUL 100ms) , **不走**隔离 |
| `fire_event` | 调用已注册回调 | `p_sock->event_cb` | **回调上下文 = netTask ** :回调内不得阻塞、不得发网络操作(会自等死锁) |
### 4.9 工具函数
### 4.10 工具函数
| 函数 | 设计作用 |
|---|---|
@@ -257,7 +286,7 @@ sequenceDiagram
Note over N: close / 网络重建 / 陈旧 resync → 显式 settle/cancel(必通知)
```
### 5.3 断连 → 自动重监听(含 Trap 23 隔离 )
### 5.3 断连 → 自动重监听(含 Trap 23 沉降 )
``` mermaid
sequenceDiagram
@@ -268,19 +297,19 @@ sequenceDiagram
Note over C: 客户端 FIN(正常)或 RST/无响应(异常)
N->>C: net_poll 读 SINT
alt DISCONNECT(正常)
N->>N: handle_disconnect_event:回读甄别 → CLOSED + 回调
N->>N: handle_disconnect_event:回读甄别 → CLOSED + 回调<br/>+ net_listener_settle(GRACEFUL 100ms)
else TIMEOUT(异常,如中止上传)
N->>N: handle_timeout_event: 置重监听隔离 + CLOSED
N->>N: handle_timeout_event: net_listener_settle(QUARANTINE 400ms) + CLOSED
end
A->>N: 下一次 net_recv(无数据)/ net_accept
N->>C: net_re listen_try → net_relisten_locked
opt 隔离 期内(Trap 23)
Note over N: 只置事实 —— 重建统一由 net_listener_pump( 100ms)决定
N->>C: net_listener _try → net_relisten_locked
opt 沉降 期内(Trap 23)
N->>C: 排空 RX 残渣(256B 分块)+ close + 排空中断
C-->>N: 未到 CLOSED/TCP_CLOSED → 返回(30ms 后重试轮询复检)
end
N->>C: set_proto_type + set_sour_port + open_socket + tcp_listen
alt open 失败(Trap 09 沉降)
N->>N: 登记重试(30ms 节奏 / 2s 时限)→ 每轮 到点重试
N->>N: 登记重试(30ms 节奏 / 2s 时限)→ net_listener_retry_tick 到点重试
else 成功
N->>N: state = LISTENING( auto_relisten 保留)
N-->>A: xTaskNotify( accept 返回 / recv 返回 0)
@@ -288,7 +317,7 @@ sequenceDiagram
Note over N: 2s 超限时 → 清状态 + "relisten give up"(错误日志,应用可感知)
```
### 5.4 丢中断自愈(Trap 22 对账)
### 5.4 丢中断自愈(Trap 22 对账 + N-32 pump )
``` mermaid
sequenceDiagram
@@ -305,7 +334,7 @@ sequenceDiagram
Note over N: 连续 2 次失配(去抖 ≈400ms)且不在冷却期 → 动手
N->>N: handle_connect_event:补 accept 簿记 → ESTABLISHED
N-->>A: 下一次 net_accept 返回客户端(连接不再"吞掉")
Note over N: 另两类:① 本层 ESTABLISHED 而芯片已断 → handle_disconnect_event<br/>③ 本层 LISTENING 而芯片监听消失 → net_re listen_try
Note over N: 另两类:① 本层 ESTABLISHED 而芯片已断 → handle_disconnect_event<br/>③④ 本层 LISTENING 丢监听 / CLOSED 孤儿 → net_listener_pump( 100ms,唯一重建入口)
```
### 5.5 网络重建(IP 变更免重启)
@@ -339,10 +368,11 @@ sequenceDiagram
| 8 | **close 前先了结挂起 recv + 排空残留 DISCONNECT ** | Trap 14/17/19:锁存的陈旧 DISCONNECT 秒断下一连接 |
| 9 | **两段式等待防丢失通知 ** :零等待拾取 + 阻塞;清除掩码全 1;以 pdTRUE 判定 | run3~11:丢失通知 → 挂死 / 结果错位;0 结果误判"无通知" → 永久挂死 |
| 10 | **网络重建先取消并通知全部挂起,再复位芯片 ** | 取消不通知 = 调用方永久挂死 |
| 11 | **监听重建只有一个所有者 ** ( `net_listener_pump` );其它位置**只置事实**(置 CLOSED / 登记沉降),不做重建动作 | N-32 P2 / B10:多所有者导致"谁先看到 DISCONNECT 就谁重建"的竞争与重建风暴 |
---
## 7. ch395f 驱动接口使用图(net_socket.c → 芯片命令)
## 7. ch395f 驱动接口使用图(` net_socket.c` / `net_listener.c` → 芯片命令)
| 芯片命令 | 使用处 | 说明 |
|---|---|---|
@@ -355,13 +385,13 @@ sequenceDiagram
| `ch395f_set_send_buf` / `ch395f_set_recv_buf` | `net_init` ( 8 Socket 布局);`net_bind` (UDP)( Socket 4~7 兜底) | 每 Socket 收 4 块 + 发 2 块 |
| `ch395f_get_glob_int_status_all` | `net_poll` ; `net_listen_locked` / `net_connect_locked` / `net_close_locked` / `net_relisten_locked` (清残留) | **读 = 清 ** (中断清除机制) |
| `ch395f_get_sock_int_status` | `net_poll` (逐 Socket SINT);各 open 路径后清残留;事件处理器清 TIMEOUT | **读 = 清 ** |
| `ch395f_open_socket` / `ch395f_close_socket` | `net_bind` (UDP)、`net_listen_locked` 、`net_connect_locked` 、`net_close_locked` 、`net_relisten_locked` (含隔离 期主动 close) | Trap 09: close 后需沉降才能重开 |
| `ch395f_open_socket` / `ch395f_close_socket` | `net_bind` (UDP)、`net_listen_locked` 、`net_connect_locked` 、`net_close_locked` 、`net_relisten_locked` (含沉降 期主动 close) | Trap 09: close 后需沉降才能重开 |
| `ch395f_tcp_listen` / `ch395f_tcp_connect` | `net_listen_locked` / `net_relisten_locked` ; `net_connect_locked` | — |
| `ch395f_set_proto_type` / `set_sour_port` / `set_des_ip` / `set_des_port` | `net_bind` (UDP)、`net_listen_locked` 、`net_connect_locked` 、`net_relisten_locked` 、`net_sendto` | open 之后写端口/IP( open 会复位寄存器);set_des_ip 期望**大端数组** |
| `ch395f_write_send_buf` | `net_send_locked` / `net_sendto` | 单次 ≤1KB(硬件 FIFO 上限,Trap 20) |
| `ch395f_get_recv_len` / `ch395f_read_recv_buf` | `net_recv_locked` 、`net_pending_tick` 、`net_pending_settle_close` 、`net_recvfrom` 、`net_drain_chip_rx` | `handle_recv_event` **故意不读 ** (防竞争,Trap 13) |
| `ch395f_get_socket_status` | `net_reconcile_tick` 、`handle_disconnect_event` (两处甄别)、`net_close_locked` (等 CLOSED)、`net_relisten_locked` ( 隔离 门)、`net_connect_locked` (诊断回读) | 输出 **2 字节 ** ( [0]=Socket 状态 [1]=TCP 状态),缓冲必须给 2 字节 |
| `ch395f_get_remot_ipp` | `handle_connect_event` 、`net_connect_locked` (诊断) | **小端**输出(与 set_des_ip 相反) |
| `ch395f_get_socket_status` | `net_listener_pump` 、 `net_ reconcile_tick` 、`handle_disconnect_event` (两处甄别)、`net_close_locked` (等 CLOSED)、`net_relisten_locked` ( 沉降 门)、`net_connect_locked` (诊断回读) | 输出 **2 字节 ** ( [0]=Socket 状态 [1]=TCP 状态),缓冲必须给 2 字节 |
| `ch395f_get_remot_ipp` | `handle_connect_event` 、`net_reconcile_tick` (幽灵甄别)、 `net_ connect_locked` (诊断) | **小端**输出(与 set_des_ip 相反) |
| `ch395f_set_keepalive_enable` | `handle_connect_event` (client 连接成功时) | — |
---
@@ -370,19 +400,20 @@ sequenceDiagram
| 想改什么 | 先看哪些函数 |
|---|---|
| 监听恢复策略(节奏/时限/隔离 ) | `net_re listen_try` / `net_relisten_locked` / `net_re listen_retry_tick` ;常量 `NET_RELISTEN_*_MS` ( `net_socket .c` 内) |
| 监听恢复策略(节奏/时限/沉降 ) | `net_listener _try` / `net_relisten_locked` / `net_listener _retry_tick` / `net_listener_pump` ;常量 `NET_RELISTEN_*_MS` / `NET_LISTENER_PUMP_PERIOD_MS` / `NET_LISTENER_STABLE_N` / `NET_LISTENER_COOLDOWN_MS` ( `net_listener .c` 内) |
| 阻塞接收等待语义 | `net_pending_register` / `net_pending_tick` / `net_pending_complete` ;常量 `NET_PENDING_WATCHDOG_MS` |
| 丢中断自愈策略 | `net_reconcile_tick` (周期/去抖/冷却三常量)+ `handle_disconnect_event` LISTENING 分支 |
| 丢中断自愈策略 | `net_reconcile_tick` (周期/去抖/冷却三常量,①②)+ `net_listener_pump` (③④,唯一重建入口 ) + `handle_disconnect_event` / `handle_connect_event` |
| 新增一个公共 API | 模式:`net_socket.h` 声明(`NET_NO_OS` 条件编译)→ `net_msg_type_t` 加类型(如需要)→ `net_process_messages` 加 dispatch 分支 → netTask 侧 `_locked` 实现;调用侧复制"参数检查 → 入队 → `net_wait_result` → errno 恢复"骨架 |
| 缓冲布局 / MSS / 重传 / KeepAlive 参数 | `net_init` + `net_config.h` + `ch395f.h` ( `CH395F_RECV/SEND_BLOCKS` 、`CH395F_TCP_MSS` );改完必须 0 错 0 警构建 + 全测试套件回归 |
| 队列容量 / 消息结构 | CubeMX 生成的 `MX_FREERTOS_Init` ( 8 槽)+ `net_socket.h` 的 `net_msg_t` ( **勿加回传字段**) |
| 超时参数(收发/重试/对账) | `net_config.h` (收发/重传/KeepAlive);`net_socket .c` 内 `NET_RELISTEN_*` / `NET_RECONCILE_*` / `NET_PENDING_WATCHDOG_MS` |
| 超时参数(收发/重试/对账) | `net_config.h` (收发/重传/KeepAlive);`net_listener .c` 内 `NET_RELISTEN_*` / `NET_LISTENER_*` ; `net_socket.c` 内 `NET_RECONCILE_*` / `NET_PENDING_WATCHDOG_MS` |
---
## 9. 相关文档
- `docs/网络/BSD_Socket_API_使用指南.md` —— 公开 API 语义、示例、常见问题(本文的上半部)
- `docs/子系统设计/网络Socket监听生命周期设计说明.md` —— **监听生命周期权威设计 ** (N-32:边沿→电平、单一所有者 pump、`net_listener.c` 模块化边界 §10)
- `docs/问题记录/CH395F_Trap_Records.md` —— Trap 01~23 机制细节(本文引用处)
- `docs/网络/Net层阻塞模型架构设计.md` —— 阻塞等待模型的现状架构设计(pending 请求模型 / 五路完成语义)
- `docs/问题记录/错误码传播审计.md` —— E-02 头阻塞与错误码回传全审计