fix(net): B2 不再丢弃 TCP_CONNECT 命令状态,命令级被拒立即回滚返 -1

问题:net_connect_locked() 调 ch395f_tcp_connect() 后直接丢弃返回值,
而同函数内 open/listen 都查了状态。§9.2.2 ⑧ 明文:发完
CMD_TCP_CONNECT_SN 后须轮询 GET_CMD_STATUS,返回 SUCCESS 仅表示
**命令**执行成功(不代表 TCP 连接成功),其他值表示命令执行失败。
丢弃它 = 命令级被拒时本层停在 CONNECTING,要等 30 s recv 超时才暴露。

修法(不能一刀切):只拦"非 SUCCESS 且非 BUSY"。因为
ch395f_poll_cmd_status() 超时返回的就是 CH395F_ERR_BUSY(= 4 s 内
芯片仍在执行,命令并未被拒绝),把它当失败会误杀慢芯片。明确失败
→ 打 [ERR] rejected by chip: 0xNN 留痕 + 按 §9.2.2 ⑧"再次连接需从
⑤ 重开"关掉 Socket、状态回滚 CLOSED、返 -1(NET_ERR_INVAL)。

顺带修一处错注释:原写"发起 TCP 连接(异步:芯片立即返回)",实际
驱动内部已按 §9.2.2 ⑧ 轮询 GET_CMD_STATUS 最多 4 s,并非立即返回。

硬件回归(Phase 7,NET 层 TCP Client 主动连接):
- TC701 连接 + 64 B 回显逐字节比对 PASS(cksum=0x000007E0)
- TC702 关闭重连 x3 全 PASS → 正常连接路径无误杀
- 不可达对端时芯片回的是 SUCCESS、ret=0 且不打 rejected by chip
  → 失败仍由 CONNECT TIMEOUT(~1.6 s) 裁定,行为未变;新分支只在
  芯片明确说"命令执行失败"时才走
- TC704 socket create #5 FAIL = E-23 既有(NET_MAX_SOCKETS=8 减 3
  个常驻监听 → 并发上限 5,而 PHASE7_MULTI_COUNT 仍是 8),与本次无关
- TC703 要求 PC 服务器离线,与 701/702/704 环境互斥,同轮连跑必有
  一侧失真;其 rc==0 断言等于把"返回值被丢弃"写进判据,真发生命令
  级被拒时它会 FAIL(属正确告警)

前两轮 Phase 7 全灭不是改动问题,已登记为台架坑:本机对
C:\programdata\anaconda3\python.exe 有两条 Public 入站 Block 规则,
而 .venv 的基座解释器正是 Anaconda python;Windows 防火墙块规则优先
于放行规则,故中途加的 8081 端口级 Allow 完全无效,只有临时关 Public
才通。症状极具误导性(芯片 1.6 s 报 CONNECT TIMEOUT、net_connect 仍
返 0、openOK 照常打印),看着像芯片/驱动故障。

残余(登记未动):net_reconcile_tick 仍只裁 ESTABLISHED/LISTENING,
CONNECTING 丢中断无人收口——产品不走出向连接、无法实测,按 Trap 25
"不加未证明的复杂度"只登记不实现。

验证:构建 0 错 0 警、Code=197032;内存快照已刷新;doc-links EXIT=0、
无 U+FFFD;@test 闭环 PASS(戳 fw build: Oct 12 2026 08:03:29
suite=NONE、回读无差异、548 ms)。测试宏已复原,两个测试头文件与
HEAD 字节一致;临时防火墙规则已删除、Public 已重新启用。
This commit is contained in:
2026-10-12 08:17:36 +08:00
parent 9a45be1700
commit 794a688a3c
3 changed files with 27 additions and 6 deletions
+3 -1
View File
@@ -939,7 +939,7 @@ Trap 22 的周期对账(200ms 节拍 + 400ms 去抖 + 1s 冷却)设计目标
| A5 | 低 | `net_socket.c:238/1005/1080/1297/1666/708/3056` | 注释与手册/HEAD 相反:`FUN_PARA(SOCKET_CLOSE=0)`(实为 bit3=1,正是 closed-with-data accept 的契约依据)、"初始化成功后设置 MAC"(§9.2.1 ① 是之前)、"复用默认缓冲"(已 8 Socket 显式分配)、"布局由 MSS 推导"(固定布局、Trap 21 已解耦)、"等 SEND_OK"(门控是 SENBUF_FREE)、UDP 头 `[0-1] reserved`(§9.2.4 是数据长度) | ✅ 已修 |
| A6 | 低 | `ch395f.c:1176` | `write_send_buf` 超限**静默 return**(void、无日志),且上限取 DMA 2048 而非发送缓冲 1024;读路径有 OVERSIZE 金丝雀、写路径没有 | ✅ 已修(补 `[ERR]` 留痕 + >1 KB 告警,行为不变) |
| B1 | ~~中~~ **已推翻** | `net_socket.c:3553` | 原判"产品路径从未启用 Socket KeepAlive → 静默断开无人知晓"(§5.56 服务器侧应在 CONNECT 后开,产品三服务全是 Server,60s/5s/3 参数空转)。**2026-10-12 实测推翻前提**:连接静置时禁用 PC 网卡(真·静默死亡),芯片 **+2 s** 即报 `[ERR][NET] sock0 TIMEOUT` → 本层 `relisten quarantine` 释放重开;2 s 恰为 §5.17 重传 8×200 ms=1.6 s 量级,**与 KeepAlive 无关**(IDLE 最短 60 s)。链路层失效本就被快速覆盖。 | ✅ 已按 §5.56 补过使能并实测,**依 Trap 25「不留未证明的复杂度」撤回**,代码内留注释记录;残余场景见下「台架注入陷阱」 |
| B2 | 中低 | `net_socket.c:1638` | `ch395f_tcp_connect()` 返回值被丢弃(同文件 open/listen 都查),命令级被拒不产生中断 → 停在 `CONNECTING`,要等 30 s recv 超时才知失败;且 `net_reconcile_tick` 只裁 ESTABLISHED/LISTENING,`CONNECTING` 无人收口 | ⏳ 待修 |
| B2 | 中低 | `net_socket.c:1638` | `ch395f_tcp_connect()` 返回值被丢弃(同文件 open/listen 都查)。§9.2.2 ⑧ 明文:`GET_CMD_STATUS` 返回 SUCCESS 仅表示**命令**执行成功、其他值表示**命令执行失败**,丢弃它 = 命令级被拒要等 30 s recv 超时才暴露 | ✅ 已修(2026-10-12):非 SUCCESS 且**非 BUSY** 才判失败——`poll_cmd_status` 超时返回的就是 `ERR_BUSY`(芯片仍在执行、命令未被拒),把它当失败会误杀慢芯片;明确失败 → `[ERR] rejected by chip: 0xNN` + 按 §9.2.2 ⑧"从 ⑤ 重开"关 Socket 回滚 + 返 `-1`。**Phase 7 实测回归**:TC701 回显 64 B 校验 PASS、TC702 重连 ×3 全 PASS(正常连接无误杀);不可达对端时芯片回的是 SUCCESS、`ret=0` 且不打 `rejected by chip`,失败仍由 `CONNECT TIMEOUT`(~1.6 s) 裁定 → 行为未变。⚠ 残余:`net_reconcile_tick` 仍只裁 ESTABLISHED/LISTENING,`CONNECTING` 丢中断无人收口(产品不走出向连接、无法实测,按"不加未证明复杂度"暂不动);TC703 断言 `rc==0` 把"返回值被丢弃"写进了判据,真发生命令级被拒时它会 FAIL(属正确告警) |
| B3 | 低 | `net_socket.c:1120-1123` | `net_listen_locked` 对 8 个 Socket 全量 `GET_INT_STATUS_SN`(读即清)→ 吞掉其它在用 Socket 的挂起事件(RECV 是电平语义可下轮补回,CONNECT/DISCONNECT 边沿语义会丢) | ⏳ 待修(只清本 Socket) |
| B4 | 低 | `net_socket.c:446/469` | 全局中断 bit0-3(`UNREACH`/`IP_CONFLI`/`PHY_CHANGE`/`DHCP`)被读清但从不消费;`ch395f_get_unreach_info()` 全仓零调用 → IP 冲突、ARP 不可达、链路抖动三类事件完全静默 | ⏳ 待修(可观测性缺口) |
| B5 | 低 | `net_socket.c:3849-3854` → `lftpd.c:803-809` | `net_getsockname` 的 `s_addr` 组装方向与本层约定(`net_inet_addr`/`net_inet_ntoa`)相反 → FTP 日志把设备自身 IP 打成 `100.1.168.192`。⚠ 同一 `getsockname` 的 PASV 227 消费点(`lftpd.c:497-503`)做了 `net_htonl` 再 MSB 拆,**双重反转恰好正确,勿误判**;风险前瞻:若把该 `s_addr` 喂给 `net_connect`/`net_sendto` 就会连到反序地址 | ⏳ 待修(改一处须同时核对 PASV) |
@@ -965,4 +965,6 @@ Trap 22 的周期对账(200ms 节拍 + 400ms 去抖 + 1s 冷却)设计目标
- 更糟的副作用:该规则会**吃掉本机后来发出的 FIN** → 设备侧留下一条**幽灵 ESTABLISHED**,之后所有新连接被 §5.29"向试图连接的远端发送 TCP RESET"直接 RST,表现为"端口拒连",容易被误判成设备故障(实为自伤)。撤规则后设备自行恢复。
- **能真正掐断链路的手段**:`Disable-NetAdapter`(链路真的 down)。⚠ 本机 PowerShell 的该 cmdlet **只有 `-Name` / `-InterfaceDescription`**,没有 `-InterfaceIndex`/`-ifIndex`;网卡名是中文,用 `(Get-NetAdapter -InterfaceIndex 19).Name` 取值再传入,避免命令行里写中文字面量。禁用/恢复必须写在 `try/finally` 里,否则网卡留在关闭状态。
- **仍未覆盖的场景**:`Disable-NetAdapter` 只能造"链路层失效";"**链路仍在、对端静默**"(NAT 会话过期、中间节点黑洞)本轮没有安全注入手段(静态 ARP 黑洞会被交换机泛洪兜底、删 IP 会连带拆掉本机连接)。这才是服务器侧 KeepAlive 唯一可能的价值场景,**未证实**,故不保留该改动。
- **主机侧回显服务器收不到入站连接 → Phase 7 全量 `CONNECT TIMEOUT`(2026-10-12 实测)**:本机对 `C:\programdata\anaconda3\python.exe` 有两条 **Public 入站 Block** 规则(`C:\python314\python.exe` 才是 Allow),而 `.venv\Scripts\python.exe` 的基座解释器正是 Anaconda python(构建报错自曝 `C:\ProgramData\anaconda3\python.exe: can't open file ...`)。Windows 防火墙**块规则优先于放行规则** → 中途加的 `New-NetFirewallRule -Direction Inbound -Action Allow -Protocol TCP -LocalPort 8081 -Profile Any` **完全无效**(实测两轮全灭),只有 `Set-NetFirewallProfile -Name Public -Enabled False` 才通。症状极具误导性:芯片 ~1.6 s 报 `CONNECT TIMEOUT`、`net_connect` 仍返 0、`[CONN] openOK` 照常打印,看着像芯片/驱动故障,实为主机防火墙。固定跑法:`try { 关 Public → 复跑 } finally { 开 Public }`(或改用 `C:\python314\python.exe` 起服务器)。
- **Phase 7 的两条既有失败与改动无关,别追**:① TC704 `socket create #5` FAIL = E-23(`NET_MAX_SOCKETS=8` 减 3 个常驻监听 → 并发上限 5,而 `PHASE7_MULTI_COUNT` 仍是 8);② TC703 要求 PC 服务器**离线**,与 701/702/704 环境互斥,同轮连跑必有一侧失真(本轮服务器在线时它反而 PASS,只因芯片先 `CONNECT TIMEOUT`)。
- **判读教训(与 Trap 25 同源)**:看到 `TIMEOUT` 先**算时序再归因**——2 s 与 60 s 一眼可分;否则会把"重传超时"误记成"KeepAlive 生效",把相关性当因果。