From 13a4a40a14320791cc35cb0eee17144f5cd8fb8e Mon Sep 17 00:00:00 2001 From: Wanderingss <1624155937@qq.com> Date: Tue, 13 Oct 2026 09:25:06 +0800 Subject: [PATCH] =?UTF-8?q?feat(net):=20=E6=9C=8D=E5=8A=A1=E5=99=A8?= =?UTF-8?q?=E4=BE=A7=20KeepAlive=20=E8=BD=AC=E6=AD=A3=EF=BC=88=C2=A75.56?= =?UTF-8?q?=EF=BC=8CE11=EF=BC=89+=20Phase=2011=20=E9=87=8D=E5=BC=80?= =?UTF-8?q?=E5=9B=9E=E5=BD=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit E11(d96113b)证据链:"链路在、对端静默"注入下生产参数 60/5/3 实测 判死 ≈30s → TIM_OUT → QUARANTINE → pump 重建;静默但存活对端不误杀。 handle_connect_event LISTENING 分支按手册规定位置开启;反汇编确认 编译器把两分支尾部合并(LISTENING 跳入含 keepalive+fire_event 的公共 尾部,hex 全片仅该分支偏移 1 行差异,Code 尺寸不变属填充巧合)。 Phase 11 重开时发现其拉入 32KB s_rx_buf 超产品 RAM 余量(L6406E 链接 失败)→ 改专用 64B 探针缓冲(本 Phase 仅 64B 探针读,门控注释已留 "勿加回"警示),套件恢复 0 错 0 警;TC-NET-1101 步骤同步死对端注入法 (禁用网卡;Windows 防火墙规则对已建立连接无效,E11 实证)。 验证:产品/套件双构建 0 错 0 警 + 反汇编确认调用落地;台架确认 (:7003 静默对端判死 + Phase 11 实跑)待设备冷启动断电重上电后补做。 --- Drivers/BSP/NET/net_socket.c | 14 ++++++------- docs/测试说明/CH395F_Test_Guide.md | 12 +++++------ docs/网络/N34-自愈审计/实验方案.md | 6 +++--- docs/问题记录/CH395F_Trap_Records.md | 10 ++++++---- test/board/ch395f_test.h | 4 +++- test/board/net_test_task.c | 30 +++++++++++++++++----------- 6 files changed, 43 insertions(+), 33 deletions(-) diff --git a/Drivers/BSP/NET/net_socket.c b/Drivers/BSP/NET/net_socket.c index 9c0ae885..0d7f0fd6 100644 --- a/Drivers/BSP/NET/net_socket.c +++ b/Drivers/BSP/NET/net_socket.c @@ -3220,13 +3220,13 @@ static void handle_connect_event(net_sock_t *p_sock) { /* 读即清:本 Socket 中断状态寄存器 */ (void)ch395f_get_sock_int_status(p_sock->ch395_sock); - /* ⚠ 曾在此按 §5.56 补 ch395f_set_keepalive_enable(sock,1)(服务器侧 - * KeepAlive),2026-10-12 实测后**撤回**:禁用网卡模拟真·静默死亡, - * 芯片 +2 s 即报 TIM_OUT(≈ §5.17 重传 8×200 ms),本层随即释放重开 - * 监听——链路层失效本就被快速覆盖,KeepAlive 的 60 s 空转窗口对 - * 该失败模式无增益。依 Trap 25 铁律「不留未证明的复杂度」撤回。 - * 未覆盖的残余场景是"链路仍在、对端静默"(NAT/中间节点黑洞), - * 本轮无法安全注入(见 Trap 记录「台架注入陷阱」)。 */ + /* §5.56:TCP 服务器须在 CONNECT 后开启 KeepAlive。2026-10-12 曾加后 + * 撤回(当时仅证明"物理断链"已被 +2s TIM_OUT 快速覆盖,其余场景 + * 无法注入、未证明);E11(2026-10-13,实验方案.md)用"禁用 PC 网卡" + * 注入"链路在、对端静默"(NAT/黑洞)补上证据链:生产参数 60/5/3 + * 实测 ≈30 s 判死 → TIM_OUT → QUARANTINE → pump 重建;对端存活时 + * 探测被 ACK、空闲会话不误杀。消费链路现成,按手册位置转正。 */ + ch395f_set_keepalive_enable(p_sock->ch395_sock, 1); /* 触发连接建立事件回调 */ fire_event(p_sock, NET_EVENT_CONNECTED); diff --git a/docs/测试说明/CH395F_Test_Guide.md b/docs/测试说明/CH395F_Test_Guide.md index c37998fa..65955bc6 100644 --- a/docs/测试说明/CH395F_Test_Guide.md +++ b/docs/测试说明/CH395F_Test_Guide.md @@ -1081,9 +1081,9 @@ IDLE → ACCEPT → [SEND: 分块发 → DONE] / [RECV: 分块收+校验 → DON | 阶段 ID | TM-PHY-11 | |---------|-----------| | **类型** | PC 配合,netTask 运行 | -| **耗时** | ~65 秒(60s idle + 15s probes) | -| **入口** | `ENABLE_PHASE11_TESTS` | -| **配置** | KeepAlive 空闲 60s,探测间隔 5s,3 次 | +| **耗时** | ~65–80 秒(60s idle + 探测窗;芯片定时语义以实测为准,见 E11) | +| **入口** | `ENABLE_PHASE11_TESTS`(2026-10-13 起默认开启;服务器侧开关由 net 层在 CONNECT 后自动置位,`docs/芯片手册/CH395F.md` §5.56) | +| **配置** | KeepAlive 空闲 60s,探测间隔 5s,3 次(`net_config.h`) | ### TC-NET-1101: KeepAlive 触发断开 @@ -1093,9 +1093,9 @@ IDLE → ACCEPT → [SEND: 分块发 → DONE] / [RECV: 分块收+校验 → DON | **优先级** | P1 | | **类型** | 功能测试 | | **标题** | TCP 连接空闲超过 KeepAlive 阈值后自动断开 | -| **前置条件** | 1. `ch395f_set_keepalive_idle(60000)`
2. `ch395f_set_keepalive_intvl(5000)`
3. `ch395f_set_keepalive_cnt(3)`
4. PC 连接后静置(不发数据) | -| **测试步骤** | 1. PC 连接 8080,不发数据
2. 等待 KeepAlive 探测(60s idle + 3×5s = 75s 最大)
3. CH395F 接收 TIMEOUT → s0 → CLOSED → LISTEN | -| **通过标准** | 超时时间 ≥ 60s,s0 回到 LISTEN | +| **前置条件** | 1. `NET_KEEPALIVE_IDLE_MS=60000` / `INTVL=5000` / `CNT=3`(`net_config.h`)
2. 对端须为**死对端**:PC 连接 8080 后**禁用网卡**制造"链路在、对端静默"(存活对端会 ACK 探测 → 永不 TIMEOUT;Windows 防火墙规则对已建立连接无效,E11 实证)
3. 勿与 Phase 6/9/10/13 混选(recv 互抢数据) | +| **测试步骤** | 1. PC 连接 8080,不发数据
2. 立即禁用 PC 网卡(中间有交换机 → 设备侧 PHY 链路保持)
3. 等待芯片报 TIM_OUT(从连接起 ≥60s;实测对端失联后判死 ≈30s)
4. 本层 QUARANTINE → pump 重建 → s0 回 LISTEN
5. 恢复网卡,重连 8080 验证端口已恢复 | +| **通过标准** | recv 返回 0 且耗时 ≥ 60s;s0 回 LISTEN 且可接受新连接 | --- diff --git a/docs/网络/N34-自愈审计/实验方案.md b/docs/网络/N34-自愈审计/实验方案.md index b7449d0b..0ec9ff49 100644 --- a/docs/网络/N34-自愈审计/实验方案.md +++ b/docs/网络/N34-自愈审计/实验方案.md @@ -433,9 +433,9 @@ CNT 次无 ACK"后产生 SINT_STAT_TIM_OUT / DISCONNECT。怀疑本仓没有利 - 静默但**存活**的空闲会话不被误杀(T2)——因为对端会 ACK 探测包。 - 2026-10-12 撤回的盲区(链路在、对端静默)**正是本机制覆盖的场景**;当年 "+2s 即报 TIM_OUT"只覆盖了物理断链。 -- **建议采纳**:在 `handle_connect_event` 的 LISTENING 分支加回一行 - `ch395f_set_keepalive_enable(sock, 1)`(手册规定位置),并重新打开 Phase 11 - 测试纳入回归;参数是否调优(如缩短 IDLE)由实测判死窗口需求决定。 +- **已采纳转正(2026-10-13,用户批准)**:在 `handle_connect_event` 的 LISTENING + 分支加回一行 `ch395f_set_keepalive_enable(sock, 1)`(手册规定位置),并重新打开 + Phase 11 测试纳入回归;参数是否调优(如缩短 IDLE)由实测判死窗口需求决定。 - ⚠ 台架副作用:本轮反复实验后设备落入 E8 残渣风暴态(详见 E12),需**冷启动 (断电重上电)**恢复,MCU 复位/RESET_ALL 不足以清除。 diff --git a/docs/问题记录/CH395F_Trap_Records.md b/docs/问题记录/CH395F_Trap_Records.md index 01dd04ae..a4852d1a 100644 --- a/docs/问题记录/CH395F_Trap_Records.md +++ b/docs/问题记录/CH395F_Trap_Records.md @@ -229,15 +229,17 @@ CH395F 的 RECV 中断是电平触发的——只要接收缓冲区有数据就 - **重传参数**:`SET_RETRAN_COUNT`(默认12次,最大20)和 `SET_RETRAN_PERIOD`(默认500ms,最大1000ms),总重传时间 = 次数 × 周期 - **KeepAlive 参数必须为 500ms 的倍数**:`IDLE`(默认20000ms)、`INTVL`(默认15000ms),且 IDLE > INTVL;传入非 500 倍数的值导致 CH395F 内部定时器异常 - **KeepAlive 默认关闭**:需在 `SINT_STAT_CONNECT` 后调用 `ch395f_set_keepalive_enable(sock, 1)` 启用 -- ⚠ **本仓现状与 E11 实测(2026-10-13)**:`ch395f_set_keepalive_enable` 全仓仅 - 客户端分支(`CONNECTING`)调用,而 `App/` 无任何 `net_connect()` → **产品上 - KeepAlive 实际全程关闭**;服务器侧调用 2026-10-12 曾加后撤回(理由仅覆盖 +- ⚠ **E11 实测与转正(2026-10-13)**:此前 `ch395f_set_keepalive_enable` 全仓仅 + 客户端分支(`CONNECTING`)调用,而 `App/` 无任何 `net_connect()` → 产品上 + KeepAlive 实际全程关闭;服务器侧调用 2026-10-12 曾加后撤回(理由仅覆盖 物理链路失效)。E11 用"禁用 PC 网卡"注入"链路在、对端静默"(防火墙规则对 已建立连接无效,见实验方案 E11 方法学),实测服务器侧开启后:生产参数 60s/5s/3 → **判死 ≈30.0s**(早于手册公式 75s,以实测为准);短参数 6s/2s/2 → ≈5.1s(证明参数生效);静默但存活的对端**不被误杀**(90s 空闲 KEEPLIVE 回显正常)。消费链路现成:TIM_OUT → `handle_timeout_event` QUARANTINE → - pump relisten → 端口恢复。完整记录见 `docs/网络/N34-自愈审计/实验方案.md` E11。 + pump relisten → 端口恢复。**2026-10-13 转正**:`handle_connect_event` + LISTENING 分支按 §5.56 开启,Phase 11 测试重新纳入回归(TC-NET-1101 步骤 + 已更新死对端注入法)。完整记录见 `docs/网络/N34-自愈审计/实验方案.md` E11。 ### Socket 4~7 缓冲区 - CH395F 默认只给 Socket 0~3 分配独立收发缓冲(共 48 块 × 512B 用满),Socket 4~7 为零块 diff --git a/test/board/ch395f_test.h b/test/board/ch395f_test.h index a59e3aa2..b11d2ee5 100644 --- a/test/board/ch395f_test.h +++ b/test/board/ch395f_test.h @@ -62,7 +62,9 @@ extern "C" { //#define ENABLE_PHASE8_TESTS /* [网] 大文件传输(100KB 文件收发) */ //#define ENABLE_PHASE9_TESTS /* [网] Select/Poll I/O 多路复用 */ //#define ENABLE_PHASE10_TESTS /* [网] 边界条件测试 */ -//#define ENABLE_PHASE11_TESTS /* [网] KeepAlive 超时断开测试 */ +#define ENABLE_PHASE11_TESTS /* [网] KeepAlive 超时断开:PC 连 8080 后须制造 + * 死对端(禁用网卡;防火墙规则对已建立连接无效, + * E11 实证);勿与 Phase 6/9/10/13 混选(recv 互抢数据) */ /* ---- 链路时延基线(M1;分段/阈值见 docs/测试说明/链路延迟测试说明.md)---- */ //#define ENABLE_PHASE12_TESTS /* [硬] SPI 字节命令 / DMA 时延基线(L1/L2,无需 PC)—— M1 已完成,基线见说明文档 §6.1 */ /* ---- 链路时延(M2/M3;分段/阈值见 docs/测试说明/链路延迟测试说明.md)---- */ diff --git a/test/board/net_test_task.c b/test/board/net_test_task.c index de50a61f..32b503dc 100644 --- a/test/board/net_test_task.c +++ b/test/board/net_test_task.c @@ -43,8 +43,10 @@ * 即首次使用 s_rx_buf)。P1-9 修复:原声明位于文件后段(Phase 8 注释块 * 之后),phase6_run 使用先于声明 → ARMCC #20 identifier undefined, * 全部 NET 层测试配置不可编译。声明条件按各 Phase 实际用缓冲精确门控: - * s_rx_buf —— Phase 6(Echo)/9(Select)/11(KeepAlive) 恒用, - * Phase 8(大文件) 非 SEND 方向用; + * s_rx_buf —— Phase 6(Echo)/9(Select) 恒用, + * Phase 8(大文件) 非 SEND 方向用,Phase 10(边界) 用; + * (Phase 11 只用 64B 探针读,2026-10-13 改专用小缓冲—— + * 产品 RAM 余量放不下 32KB,勿再把 11 加回本门控) * s_tx_buf —— Phase 8(大文件) 非 RECV 方向(填充发送窗口)及 * Phase 10(边界) 用。 * PHASE8_DIRECTION 由下方方向配置块定义(仅 ENABLE_PHASE8_TESTS 时); @@ -66,8 +68,7 @@ #if defined(ENABLE_PHASE6_TESTS) || \ (defined(ENABLE_PHASE8_TESTS) && (PHASE8_DIRECTION != PHASE8_DIR_SEND)) || \ - defined(ENABLE_PHASE9_TESTS) || defined(ENABLE_PHASE10_TESTS) || \ - defined(ENABLE_PHASE11_TESTS) + defined(ENABLE_PHASE9_TESTS) || defined(ENABLE_PHASE10_TESTS) static uint8_t s_rx_buf[TEST_BUF_SIZE]; #endif /* s_tx_buf 仅 SEND/ECHO(及 Phase 10)需要;RECV 不发送,避免未使用变量警告 */ @@ -791,19 +792,24 @@ static void phase10_run(int32_t sockfd) { /* =================================================================== * Phase 11 — TCP KeepAlive 超时断开 * - * 测试目的:验证 CH395F 的 TCP KeepAlive 机制: - * 1. 连接空闲超过 idle 阈值(60s)后开始发送探测包 - * 2. 探测无响应(默认 3 次 × 5s = 15s)后触发 TIMEOUT - * 3. Socket 自动关闭并回到 LISTEN + * 测试目的:验证 CH395F 的 TCP KeepAlive 机制(服务器侧开关已由 net 层 + * 在 CONNECT 事件后按 §5.56 转正开启,E11 2026-10-13): + * 1. 连接空闲超过 IDLE(60s)后芯片开始发送探测包 + * 2. CNT × INTVL(3 × 5s)无 ACK → SINT_STAT_TIM_OUT + * 3. 本层 QUARANTINE → pump 重建 → 回 LISTEN * - * 入口条件:Socket 0 处于 ESTABLISHED,PC 连接后静置(不发数据)。 - * 通过:KeepAlive 断开时间 ≥ 60s(从连接建立到 recv 返回 0 的间隔)。 - * 失败:不到 60s 就断开(可能是其他原因导致)或 75s 后仍未断开。 + * 入口条件:Socket 处于 ESTABLISHED 且对端为**死对端**——PC 连 8080 后 + * 禁用网卡(存活对端会 ACK 探测、TIMEOUT 永不触发,本测试将 + * 一直等待;防火墙规则对已建立连接无效,E11 实证)。 + * 通过:从连接建立到 recv 返回 0 的间隔 ≥ 60s(IDLE 阈值)。 + * 注:芯片定时语义与手册公式(75s)不完全一致,实测判死窗口见 + * 实验方案 E11(对端失联后 ≈30s)。 * =================================================================== */ #ifdef ENABLE_PHASE11_TESTS static int32_t s_phase11_armed = 0; static uint32_t s_phase11_start = 0; +static uint8_t s_phase11_buf[64]; /* 专用小缓冲:不占 32KB s_rx_buf(RAM 余量) */ static void phase11_run(int32_t sockfd) { /* @@ -818,7 +824,7 @@ static void phase11_run(int32_t sockfd) { return; } - int32_t n = net_recv(sockfd, s_rx_buf, 64, NET_MSG_DONTWAIT); + int32_t n = net_recv(sockfd, s_phase11_buf, 64, NET_MSG_DONTWAIT); if (n == 0) { uint32_t elapsed = HAL_GetTick() - s_phase11_start; TEST_CHECK(elapsed >= 60000,