b5671e8c02
真机第一轮(2026-11-04 08:16,阶段 16 台架)把本轮最大的一条设计错误照了出来: TC-NET-1603 在客户端形态下发 48 字节应答,板子读到的是 `n=56`、首字节 `0x38` ——56 正是信息头的长度字段,也就是**头还在**,而我把它的 8 字节当成了载荷。 设计时按手册 §9.2.4 读出一句"客户端模式不带信息头",就以为运行时把 DesIP 从 全 F 改成具体对端会让头消失。真机给的定案是:**加不加 8 字节信息头,是在 OPEN_SOCKET 那一刻由当时的 DesIP 定死的**,之后再改 DesIP 只改发送去向与芯片 的收包源过滤,不改收包帧格式。本层 `net_bind` 一律以 DesIP=0xFFFFFFFF 开 Socket,所以对本层的 UDP socket 而言头恒在、恒剥。 - `net_udp_datagram_read` 的剥头条件由 `udp_mode == SERVER && avail >= 8` 改为 **只看 `avail >= 8`**;源地址一律从信息头里取——头里的源 IP/端口是本包的真实 来源,比"我把包发给了谁"这本账更准(NAT 后的时间服务器就是两种地址不一致的 那种)。 - 不足 8 字节的残包保持既有定案(TC-NETSOCK-440o):无从判头,整包按载荷交付, 源地址退到对端记账。这也是 `net_udp_src_from_peer()` 如今唯一的读者。 - `udp_mode` 语义收窄为"芯片的对端过滤 + 发送去向",**不描述收包帧格式**; `net_types.h` 的 UDP 形态块、两个模式宏的注释、`net_sendto` / `net_recvfrom` 的函数说明一并按定案改写。字段本身没动,仍复用原有的 2 字节 padding,RAM 零增长。 产品构型 Keil 构建 0 Error 0 Warning,`Code=249120 RO=29836 RW=2052 ZI=149996`, 与改造前的产品基线同尺寸。判据文字与真机证据见同批的测试、文档提交。
Drivers/BSP/NET/ — 网络抽象层(BSD Socket API)
在 CH395F 硬件驱动之上提供 BSD Socket 风格 API,使上层(FTP 服务、时间同步、UI 推流) 以标准 socket 语义编程,无需关心芯片细节。核心是阻塞模型 + 事件驱动 + 周期对账自愈。
文件一览
| 文件 | 作用 |
|---|---|
net_socket.c/h |
BSD Socket API 实现:net_socket / net_connect / net_send / net_recv / net_close / net_listen / net_accept,含挂起 recv(pending)与错误码 |
net_pending.c/h |
挂起 recv / send 请求表(阻塞挂起模型:登记 / 每轮完成检查(recv)/ 推进(send)/ 取消 / close 结算;真机专属,模拟器走 net_socket_sim.c) |
net_select.c/h |
select/poll 多路复用(基于芯片中断事件 + 周期轮询) |
net_types.h |
网络层公共类型定义 |
net_config.h |
网络层编译期配置(缓冲区大小、超时、对账周期等) |
lftpd/ |
轻量 FTP 服务器(第三方移植,见其子目录) |
核心机制
阻塞模型(挂起 recv / send / pending)
- recv 挂起(pending)已落地:
net_recv阻塞不占 netTask——登记挂起请求 + 每轮 tick 唤醒 - send 挂起已落地:
net_send快路径有界自旋(NET_SEND_SPIN_MS= 2ms),超预算登记 挂起 send,由net_send_pending_tick按主循环节拍分块推进;netTask 最坏停滞由 1s 压到 2ms - ACCEPT 决定不挂起化(2026-10-15)(调用方轮询问题,不是队头阻塞;pending 化收益仅 RPC 开销与抖动)
- 架构细节:
docs/网络/Net层阻塞模型架构设计.md
周期对账自愈(TRAP-NET-17 修复;对账与监听重建合一)
net_reconcile_tick()(100ms,判据读驱动状态缓存——唯一 刷新点net_poll每轮全量刷新,E2 实测单次回读 18.1us)修正失同步: 本层 ESTABLISHED 而芯片已断 → 断开;本层 LISTENING 而芯片已握手 → 补 accept; 本层 LISTENING/CLOSED(监听型 fd)而芯片监听消失 → 重建(形态③④); 本层 CLOSED 而芯片在听 → 只重同步(U11);芯片静默回收 → RESYNC(⑤)- 分级去抖(②=1 轮 / ③④⑤=2 轮 / ①=4 轮)+ 冷却(1s)——监听失位的重建
动作必须去抖,否则"刚 listen 未生效"瞬态会反复重建直至
relisten retry budget exhausted(三监听口全死);①的 4 轮窗是风暴态三机制里 M3 可疑门的让位租约(见docs/网络/网络自愈架构设计.md§4.1.6,勿改小)
异常复监听隔离(TRAP-NET-18 修复)
- 异常释放(TIMEOUT)的 socket 复监听前隔离:排空 RX 残渣 + 主动 close + 等 CLOSED
- 400ms 起;800ms 上限只留痕、不强行放行(芯片未回 CLOSED 时 open 注定吃 USE 错, 真正兜底 = D10 预算到期 + 对账形态④ 重新登记)
- 仅异常路径生效,正常 FIN 路径零改动
错误码
- 按任务隔离:
s_net_errno_slots[8](每槽{任务句柄, err}),替代原全局s_net_errno net_get_errno()只读本任务槽位,对外语义不变(成功不清零)
依赖关系
上层(FTP / timeSync / UI 推流)
↓ BSD Socket API
net_socket.c ←→ net_select.c
↓ ch395f_* 接口
Drivers/BSP/CH395F/
⚠️ 改本模块前必读
- 就绪屏障位(
SYS_EVT_NET_READY等)等待必须osFlagsNoClear,不能用默认消费型 - 每次 CH395F SPI 事务须
ch395f_spi_begin()/end()包裹 - 改任何自愈/重建逻辑必须去抖,否则会引发重建风暴
- 相关陷阱:
docs/问题记录/CH395F_Trap_Records.mdTRAP-NET-17/-18
相关文档
- 上层用法:
docs/网络/BSD_Socket_API_使用指南.md - 内部调用关系:
docs/网络/BSD_Socket_API_函数调用关系.md - 架构设计:
docs/网络/Net层阻塞模型架构设计.md(阻塞等待 / pending 请求模型) - ACCEPT 挂起化不实施的判据:
docs/网络/Net层阻塞模型架构设计.md§10 - 测试:
docs/测试说明/板级测试/CH395F_Test_Guide.md、docs/测试说明/板级测试/FTP_Test_Guide.md