4c21cff753
现象:抓自检台账基线那次 FTP 会话一结束,设备就打 `[ERR] [FTP] error reading next command`, 本次 `@test` 直接 FAIL(Forbidden 含 `[ERR]`)。查下去发现 lftpd 把**全部**异常路径一律按 error 级打,其中九处其实由外部输入决定:客户端关窗口、连上就走、PASV 后不建数据连接、 运维改 IP 触发监听重建。两条代价:① 任何人"连 21 端口一下就断开"就能把一次启动测试打成 FAIL;② 同样的动作可以**无限制造固件级 ERROR 日志**——日志面与门禁面都可被外部驱动, 工业现场过不了审。 **做法**:`private/lftpd_log.h` 新增 `lftpd_log_peer()`(info 级,带 `[FTP]` 前缀),而不是 就地改成 `DBG_INFO`——让"谁才算真故障"在读代码时一目了然,也防止后来者顺手把对端行为升回 error。降级的九处:控制通道被对端关闭(原 `error reading next command`,改为 `control channel closed by peer (ret=%d)`,措辞原先把正常收尾写成故障、误导过排查方向)、 发送 220 前对端已走、`listener rebuilt`(网络重建,原因由 accept 层自己记)、四处 `data accept failed`(PASV 后没来数据连接)、`accept timeout`。 **刻意留在 error 的**:create/bind/listen/取端口失败、写通道失败、文件打开失败、 `DOWNLOAD/UPLOAD FAILED`(对端中途退出确实让一次传输没走完 = 数据完整性事件, `../问题记录/CH395F_Trap_Records.md` 的中止家族要靠这些锚点定位)、 `data listener disarmed`(控制连接还活着而我方数据监听已不在服役 = 本侧异常,就地注明)。 **实测划清的边界**:客户端**干净收尾**(发 `QUIT` 或 FIN 半关)现在整条链路没有任何 `[ERR]`; 但 **RST 硬拆**(`SO_LINGER` 清零 / 进程被杀 / 拔线)仍会留一条 `[ERR][NET] sockN TIMEOUT`—— 那是 net 层的套接字超时锚点,网络自愈架构判据要读它,不在本次降级范围。所以"采集窗口内别跑 `data listener disarmed`(控制连接还活着而我方数据监听已不在服役 = 本侧异常,就地注明)。 **真机验证**:`@build` 0 错 0 警告;`@test --require-selftest-pass --device-ip` → **PASS** (基线 29 条 → 本次无新增;`net init: OK` 726 ms);专门写了 RST 复现脚本(登录后 `SO_LINGER` 清零直接关)→ 串口得到 `[FTP] control channel closed by peer (ret=-1)`,**不再是 `[ERR]` 行**; 同一份日志里 QUIT 收尾的那次会话完全没有错误行。 体量:`Code` 213,236 → **213,264 B(+28)**、`RO` 18,580 → **18,652 B(+72)**(措辞变长), 同一份日志里 QUIT 收尾的那次会话完全没有错误行。 `ZI` 131,500、`RW_IRAM1` 75,264 B 57.4% / `RW_CCM` 57,376 B 87.9% 全不变)。数字联动照做: 两份 `data/` 快照、`ram_baseline --update/--write`、`check_mem_numbers --write`、FLASH 配图 常量重算重生成(桶合计 = 231,916 与 `Code+RO` 相符;越界复查 0)、报告 §1.3/§5.1/§5.4 与 部署文档 §3.3 同步。 口径落文:新增 `docs/架构设计/STM32F4-Base存储架构说明.md` §10.3「FTP 日志级别划分:对端行为 vs 固件故障」(含两类的判据表与边界),并更新 `docs/测试说明/自动化测试流程.md` 的采集窗口警示。 顺手把 `private/lftpd_log.h` 工作区的 CRLF 混用归一回 LF(仓库里 blob 本来就是 LF,diff 无影响)。 门禁:`doc-links --all --sections-strict` 146 文档 0 问题、`id-check --strict` 增量拦截通过 (可判定 82.82%)、`mem-numbers` / `mem-budget` / `layer-deps` 全绿;改动文件字节卫生 BOM 无 / CR 0 / U+FFFD 0 / 无行尾空格。未推送。
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 轮窗是 F-NET-17 M3 可疑电平的让位租约(勿改小)
异常复监听隔离(TRAP-NET-18 修复)
- 异常释放(TIMEOUT)的 socket 复监听前隔离:排空 RX 残渣 + 主动 close + 等 CLOSED
- 400ms 起;800ms 上限只留痕、不强行放行(芯片未回 CLOSED 时 open 注定吃 USE 错, 真正兜底 = D10 预算到期 + 对账形态④ 重新登记)
- 仅异常路径生效,正常 FIN 路径零改动
错误码(E-03 修复)
- 按任务隔离:
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/任务规划/基线与待确认.md§2.1 - 测试:
docs/测试说明/CH395F_Test_Guide.md、docs/测试说明/FTP_Test_Guide.md