From 794a688a3c89dce592bb0bb73723edc9c1a246bd Mon Sep 17 00:00:00 2001 From: Wanderingss <1624155937@qq.com> Date: Mon, 12 Oct 2026 08:17:36 +0800 Subject: [PATCH] =?UTF-8?q?fix(net):=20B2=20=E4=B8=8D=E5=86=8D=E4=B8=A2?= =?UTF-8?q?=E5=BC=83=20TCP=5FCONNECT=20=E5=91=BD=E4=BB=A4=E7=8A=B6?= =?UTF-8?q?=E6=80=81=EF=BC=8C=E5=91=BD=E4=BB=A4=E7=BA=A7=E8=A2=AB=E6=8B=92?= =?UTF-8?q?=E7=AB=8B=E5=8D=B3=E5=9B=9E=E6=BB=9A=E8=BF=94=20-1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题: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 已重新启用。 --- Drivers/BSP/NET/net_socket.c | 25 ++++++++++++++++++++++--- docs/内存管理/RAM使用分析与优化方案.md | 4 ++-- docs/问题记录/CH395F_Trap_Records.md | 4 +++- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/Drivers/BSP/NET/net_socket.c b/Drivers/BSP/NET/net_socket.c index fc875806..c8f08221 100644 --- a/Drivers/BSP/NET/net_socket.c +++ b/Drivers/BSP/NET/net_socket.c @@ -1526,7 +1526,8 @@ int net_connect(int sockfd, const struct net_sockaddr *addr, int addrlen) { * 函数说明:1. 校验控制块(in_use、TCP 类型、地址参数)并保存对端地址 * 2. 芯片侧 OPEN(失败重试 3 次、间隔 500ms,见 Trap 09)+ 配置源/目的端口与目的 IP * 3. 清本 Socket 残留中断状态(丢弃上一条连接遗留的 DISCONNECT) - * 4. 发起 TCP_CONNECT 并置 CONNECTING;成败由 net_poll() 的 CONNECT/TIMEOUT 中断裁定 + * 4. 发起 TCP_CONNECT(驱动内已轮询 CMD_STATUS)并置 CONNECTING; + * 命令级被拒立即关 Socket 返 -1,TCP 层成败仍由 CONNECT/TIMEOUT 裁定 */ static int net_connect_locked(int sockfd, const struct net_sockaddr *addr, int addrlen) { /* Socket 控制块指针 */ @@ -1634,8 +1635,26 @@ static int net_connect_locked(int sockfd, const struct net_sockaddr *addr, int a (dip >> 8) & 0xFF, dip & 0xFF, dp, st[0], st[1]); } - /* 发起 TCP 连接(异步:芯片立即返回) */ - ch395f_tcp_connect(sockfd); + /* 发起 TCP 连接:驱动内部已按 §9.2.2 ⑧ 轮询 GET_CMD_STATUS(4 s 上限), + * 并非"立即返回"。⚠ §9.2.2 ⑧ 明文:返回 SUCCESS 仅表示**命令**执行成功, + * 不代表 TCP 连接成功;连接结果仍由 CONNECT/TIM_OUT 中断裁定。 */ + status = ch395f_tcp_connect(sockfd); + + /* BUSY 是 poll 超时的返回值 = 芯片仍在执行,命令并未被拒绝 → 维持原行为, + * 交给 CONNECT/TIM_OUT 裁定,不得当成失败(否则慢芯片会被误杀)。 + * 其余非 SUCCESS 均为明确的命令级失败(VAL/ARG/USE/OPEN/IF/MEM/BUF/RTE…): + * 此前返回值被直接丢弃 → 本层停在 CONNECTING 干等 recv 30 s 超时才暴露 + * (2026-10-12 审计 B2)。按 §9.2.2 ⑧"再次连接需从 ⑤ 重开"回滚。 */ + if ((status != CH395F_ERR_SUCCESS) && (status != CH395F_ERR_BUSY)) { + DBG_ERROR("[CONN] sock%d TCP_CONNECT rejected by chip: 0x%02X", sockfd, status); + (void)ch395f_close_socket((uint8_t)sockfd); + p_sock->state = NET_SOCK_STATE_CLOSED; + return NET_ERR_RETURN_1(NET_ERR_INVAL); + } + if (status == CH395F_ERR_BUSY) { + DBG_INFO("[CONN] sock%d TCP_CONNECT still BUSY after poll, await irq", sockfd); + } + /* 状态 → CONNECTING(等待 CONNECT/TIMEOUT 中断裁定) */ p_sock->state = NET_SOCK_STATE_CONNECTING; diff --git a/docs/内存管理/RAM使用分析与优化方案.md b/docs/内存管理/RAM使用分析与优化方案.md index 95325ee6..3af483cd 100644 --- a/docs/内存管理/RAM使用分析与优化方案.md +++ b/docs/内存管理/RAM使用分析与优化方案.md @@ -36,10 +36,10 @@ | 区域 | 用量 | 容量 | 占比 | |---|---|---|---| -| ER_IROM1 | 213,696 B (208.69 KB) | 1,048,576 B (1024 KB) | 20.4% | +| ER_IROM1 | 213,956 B (208.94 KB) | 1,048,576 B (1024 KB) | 20.4% | | RW_IRAM1 | 94,768 B (92.55 KB) | 131,072 B (128 KB) | 72.3% | | RW_CCM | 49,184 B (48.03 KB) | 65,280 B (64 KB) | 75.3% | -| **Flash (ROM 合计)** | **214,664 B (209.6 KB)** | 1,048,576 B (1024 KB) | **20.5%** | +| **Flash (ROM 合计)** | **214,924 B (209.9 KB)** | 1,048,576 B (1024 KB) | **20.5%** | 各区的容量与常量来源: diff --git a/docs/问题记录/CH395F_Trap_Records.md b/docs/问题记录/CH395F_Trap_Records.md index efe379ac..8cefb088 100644 --- a/docs/问题记录/CH395F_Trap_Records.md +++ b/docs/问题记录/CH395F_Trap_Records.md @@ -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 生效",把相关性当因果。