From a3c6e89378479cf21e41e6572e3511a1d97f3199 Mon Sep 17 00:00:00 2001 From: Wanderingss <1624155937@qq.com> Date: Sun, 25 Oct 2026 10:08:46 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E7=BD=91=E7=BB=9C):=20=E4=B8=BB=E5=8A=A8?= =?UTF-8?q?=E6=8B=86=E4=BC=9A=E8=AF=9D=E7=99=BB=E8=AE=B0=E5=8D=B3=E8=BF=94?= =?UTF-8?q?=E2=80=94=E2=80=94drop=20=E8=B7=AF=E5=BE=84=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E5=8D=A0=E7=94=A8=20netTask=EF=BC=88=E7=99=BB=E8=AE=B0?= =?UTF-8?q?=E7=B0=BF=20N-17=20=E6=94=B6=E5=8F=A3=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 道F 批 2 / task-17。定位 + 单点收口,不开 net 层跨上下文大口子。 【定位(链路,全部代码可复核)】 探针挂起 30s → time_sync.c:149 预算耗尽 → :187 net_drop_conn()。NET_MSG_ DROP_CONN 处理器(net_drop_conn_locked)内同步跑 net_sock_reset_sync (QUARANTINE)(2ms 节奏、400ms 底/600ms 顶,自家注释原记「等待期 netTask 有界占用 ~400-600ms」):netTask 的 1ms 主循环冻结在同一迭代 → NET 位 停摆;串行总线整窗停摆 → UI 推流/FTP/104 在途消息回执顺延;对时调用方 睡在 net_wait_result 等回执期间无分片心跳 → 五位同窗缺席 → 连续 8 个 100ms 抽样停喂 → 声明 [WD] task stall(V5 时代 req=0x37、快照只剩 bit0, 即日志 mask=0x00000001)。被动断连/EOF 走 net_listener_tick 异步沉降机 (D10 条令)故纯 FTP 负载 0 stall——只有主动 drop 留在同步原语里。 【修复】监听型 fd 的芯片拆除改为 net_listener_settle(QUARANTINE) + net_listener_request(REQ_DROP) 登记即返,拆链/排渣/等 CLOSED/重建全部 交 net_listener_tick 30ms 节奏——与被动释放同一台机器(E16 台架实证该 自建序列 RST 10/10、FIN 5/5)。非监听 client fd 无监听可保,同步形态 原样保留(全仓零生产调用者)。返回值语义显式改为「拆除与重建已登记」 (net_socket.h 注释按 lead 要求写明,防未来调用方按旧语义写)。 APP_ASSERT(S12 试点)分派门与断言位置未动,登记时序下仍成立。 【评估否决留档(lead 要求论证入文档)】「缩短重建重试 5×2s」不采: 该退避在 time_sync_open_listener() bind/listen 失败路径、已被 time_sync_delay_fed() 分片喂保护、不在缺口链上;缩短反撞 TRAP-NET-09 沉降窗。全文见 docs/网络/网络自愈架构设计.md §4.6(已改写)。 【竞态与信源核对】① 主路径置 CLOSED 后迟到的 DISCONNECT/TIMEOUT 被 net_events 状态门拦截(只认活会话态),不会把刚登记的 QUARANTINE 降级; ② 零新增读清点——全局寄存器唯一读者仍 net_poll,隔离窗两处本 Socket 读清是 net_listener_tick 原位,§4.10 三条准入判据不触发;③ E13 物理 边界(对端不配合 FIN_WAIT_2 长尾)逐字不变,只移动等待发生的位置。 【测试】net_socket 主机套件 457/457:TC-NETSOCK-240f/321b/321b2/460e 重锚到登记式(settle+REQ_DROP、rst 机零启动、tick 预算 ≤20、无 [ERR]), 新增 321g/h/i + 460f 钉住 client fd 旧同步形态。全量 --all-files 40 绿。 【体积增量(vs scripts/ram_baseline.tsv 2026-10-25 行)】 ER_IROM1 237328→237344(+16 B);RW_IRAM1 75792(+0);ZI 0。 @build 0 错误 0 警告(Code=215948 RO=21396 RW=1232 ZI=131984)。 gate: only mem-budget/mem-numbers pending lead ledger backfill test-coverage 本条**绿**(已覆盖行净增,未动用裁决③的覆盖率例外—— 旧同步路径行经 client fd 新用例继续被覆盖,净差非负)。其余含 id-check/doc-links/全部主机单测绿。仅 mem-numbers/mem-budget 两红 = 台账 +16B 漂移待回填(lead 单写者禁区)。 【越界申报】① test/host/net_socket/{ns_s3_msg,ns_s4_state,ns_s5_io}.c (批准③同类:只动本任务相关用例,未碰他人行);② docs/网络/ 网络自愈架构设计.md §4.6 改写(lead 本任务指令明示)。登记簿 N-17 销项与 README 波次行归 lead;docs/内存管理 数字未碰(禁区)。 台架:V5 复跑判据(e02_probe + FTP ≥30min 零 stall / 零复位)属网络 对端类——待 lead 排窗,步骤清单随 done 消息发出。 --- Drivers/BSP/NET/net_socket.c | 32 +++++++++++++++++----- Drivers/BSP/NET/net_socket.h | 15 +++++++--- docs/网络/网络自愈架构设计.md | 44 ++++++++++++++++++++++++------ test/host/net_socket/ns_s3_msg.c | 6 ++-- test/host/net_socket/ns_s4_state.c | 21 ++++++++++++-- test/host/net_socket/ns_s5_io.c | 16 +++++++++-- 6 files changed, 108 insertions(+), 26 deletions(-) diff --git a/Drivers/BSP/NET/net_socket.c b/Drivers/BSP/NET/net_socket.c index d67e7c67..4073b50f 100644 --- a/Drivers/BSP/NET/net_socket.c +++ b/Drivers/BSP/NET/net_socket.c @@ -758,7 +758,9 @@ void net_process_messages(void) { break; } case NET_MSG_DROP_CONN: { - /* E-02 收口:应用放弃会话的主动拆除(拆连接、保监听) */ + /* E-02 收口:应用放弃会话的主动拆除(拆连接、保监听)。监听型 + * 登记即返(N-17:拆链排渣交 net_listener_tick,本消息不再占用 + * netTask 数百 ms) */ net_sock_t *p_sock = net_get_sock(msg.sockfd); if (p_sock == NULL) { /* 描述符无效 → BADF */ @@ -2044,8 +2046,11 @@ static int net_close_locked(net_sock_t *p_sock) { * (PC 内核自动 ACK keepalive,僵尸判不死),单连接监听被永久 * 占住;应用又不得 net_close 监听 fd(毁监听)——本函数补上 * "拆连接不拆监听"的应用侧形态 - * 2. 拆除=sock_reset QUARANTINE(TRAP-NET-18 异常释放语义:FIN + 排渣 + - * 排空事件(quiescent) + 等 byte0=00H,内置二段 CLOSE 补发) + * 2. 芯片侧拆除(TRAP-NET-18 异常释放语义,N-17 收口改异步):监听型 fd + * = net_listener_settle(QUARANTINE) + REQ_DROP 登记即返,动作全部交 + * net_listener_tick(隔离窗内持续排空/关闭正是该机的既有语义); + * 非监听 client fd = 同步 sock_reset QUARANTINE(600ms 封顶,原形态) + * ——本处理器不得再对监听型做同步等待(netTask 占用窗见 .h 注) * 3. auto_relisten(监听型 fd):收口 CLOSED 后登记重建请求(动作在 * net_listener_tick,≤1ms 后执行;芯片已 CLOSED 直接 open 必成, * E4 定案;未成则 30ms 节奏重试 + reconciler 形态④兜底); @@ -2079,8 +2084,20 @@ static int net_drop_conn_locked(net_sock_t *p_sock) { /* 挂起 send 同样先结束(以已发字节数通知;未发部分放弃),再拆芯片会话 */ net_send_pending_cancel((int)(p_sock - s_net_socks), NET_ERR_CONNRESET); - /* 确定性拆除(异常释放 → QUARANTINE 400ms 底,TRAP-NET-18 语义) */ - if (net_sock_reset_sync(sockfd, CH395F_RESET_QUARANTINE) != 0) { + /* 芯片侧拆除按 fd 形态分流(异常释放 = QUARANTINE 语义,TRAP-NET-18)。 + * 监听型 fd:只登记沉降隔离 + 重建请求,**立即返回**——“排渣/FIN 关闭/ + * 排空事件/等 byte0=00H/重建”整套动作由 net_listener_tick 以 30ms 节奏执行 + * (与被动超时释放同一台机器;E16 台架实证该 net 层自建序列 = RST 10/10、 + * FIN 5/5 的通过版本)。旧形态在本处理器里 net_sock_reset_sync 同步等待, + * netTask 被占用 400ms 底/600ms 顶:串行总线整窗停摆、UI/FTP/对时的 API 回 + * 执顺延、NET 与对时位同窗丢 100ms 抽样 → 800ms 停喂声明 stall,距 IWDG + * 最坏超时余量仅 ~0.7s(登记簿 N-17;方向评估与否决论证 → 《网络自愈架构 + * 设计》§4.6)。返回语义随之明确:返回 = 已登记,芯片未必已拆完。 + * 非监听 client fd:无监听可保,维持同步确定性排渣(600ms 封顶;全仓 + * 零生产调用者,形态原样保留防漂移)。 */ + if (p_sock->auto_relisten != 0U) { + net_listener_settle(sockfd, NET_LISTENER_SETTLE_QUARANTINE); + } else if (net_sock_reset_sync(sockfd, CH395F_RESET_QUARANTINE) != 0) { DBG_ERROR("sock%d drop_conn: sock_reset not done", sockfd); /* 超时继续收口本层;芯片侧交 reconciler/pump 对账接管 */ } @@ -2424,8 +2441,9 @@ int net_close(int sockfd) { * 函数说明:1. 单连接模型下挂起但 TCP 仍活的客户端会永久占住监听(keepalive * 被 PC 内核自动 ACK 判不死,《网络自愈架构设计》§5.2 E13);应用不得 * net_close 监听 fd → 本 API 拆连接不拆监听(E-02 收口) - * 2. netTask 侧执行 sock_reset QUARANTINE + 监听即时重建(见 - * net_drop_conn_locked);等待期 netTask 有界占用 ~400-600ms + * 2. 监听型 fd:netTask 侧仅登记隔离沉降 + 重建请求即返回(动作在 + * net_listener_tick,netTask 零占用,登记簿 N-17 收口);非监听 client + * fd 仍同步排渣(600ms 封顶)。见 net_drop_conn_locked */ int net_drop_conn(int sockfd) { /* 消息体(按值拷入队列,netTask 改的是其副本) */ diff --git a/Drivers/BSP/NET/net_socket.h b/Drivers/BSP/NET/net_socket.h index d085cd7e..ac0688df 100644 --- a/Drivers/BSP/NET/net_socket.h +++ b/Drivers/BSP/NET/net_socket.h @@ -396,15 +396,22 @@ int net_close(int sockfd); /* * 函数功能:主动拆掉当前 TCP 会话,**保留监听**(E-02 收口) * 入口参数:sockfd - socket 描述符 - * 返回值:0 成功(含幂等:无活动会话),-1 失败 + * 返回值:0 成功(**监听型 fd 的“成功”= 拆除与重建已登记**,芯片未必已拆完, + * 见函数说明 2;含幂等:无活动会话),-1 失败 * 限定条件:非 netTask 上下文(经消息队列委托,同其它 net_* API) * 函数说明:1. 适用场景=应用层放弃会话(对时 30s 预算耗尽、协议超时等): * 单连接模型禁止对监听 fd net_close(连监听一起毁,AGENTS 约定), * 而挂起但 TCP 仍活的客户端会永久占住单连接监听(PC 内核自动 * ACK keepalive,芯片僵尸判不死——《网络自愈架构设计》§5.2 E13 定案缺口) - * 2. 实现=sock_reset QUARANTINE(FIN 关闭 + 排渣 + 排空事件 + 等 - * byte0=00H,TRAP-NET-18 异常释放语义);auto_relisten fd 随即重监听 - * (失败交 D10 重试),非 auto_relisten(client fd)收口 CLOSED + * 2. 实现(2026-10-25 N-17 起):auto_relisten fd = 登记 QUARANTINE 隔离 + * 沉降 + REQ_DROP 重建请求,**登记即返**——“FIN 关闭 + 排渣 + 排空 + * 事件 + 等 byte0=00H”(TRAP-NET-18 异常释放语义)与重建由 + * net_listener_tick 按 30ms 节奏异步执行,端口恢复窗 ~0.4-1s 与被动 + * 断连同型;**调用方不得按旧语义假设“返回即芯片已 CLOSED”**——返回 + * 后 accept/recv 得 WOULDBLOCK 直至重建完成(time_sync 既有轮询天然 + * 容忍)。旧同步形态曾使 netTask 占用 400-600ms、五任务心跳同窗 + * 缺席声明 stall(《网络自愈架构设计》§4.6 N-17 记录,勿复活)。 + * 非 auto_relisten(client fd)无监听可保,仍同步排渣(600ms 封顶) * 3. 幂等:非 ESTABLISHED/CONNECTING 直接返 0,不动监听 */ int net_drop_conn(int sockfd); diff --git a/docs/网络/网络自愈架构设计.md b/docs/网络/网络自愈架构设计.md index 61317c14..3a7dc546 100644 --- a/docs/网络/网络自愈架构设计.md +++ b/docs/网络/网络自愈架构设计.md @@ -422,17 +422,45 @@ CLOSED → 由形态④ 接手。关键在两条快路径的判据(`net_pendin 必须 `net_select` 超时或 `net_recv_to` 显式预算。 - 上层 32 轮盲收(`ui_net_sink` / `iec104_serv`)保留现状(E6 裁决,§4.8)。 -### 4.6 `net_drop_conn`——应用主动拆会话保监听(E-02 收口) +### 4.6 `net_drop_conn`——应用主动拆会话保监听(E-02 收口;N-17 异步收口 2026-10-25) -- 语义:`net_pending_settle_close` 先结束挂起 recv(回传结果 + 唤醒调用方)→ - `net_sock_reset_sync(QUARANTINE)` → 本层收口 CLOSED(不清 fd)→ `auto_relisten` 即时登记 - 重建请求(`net_listener_tick` 执行;失败交 D10 + 形态④)。 +- 语义(**返回值 = 拆除与重建已登记**,不代表芯片已拆完):`net_pending_settle_close` + 先结束挂起 recv(回传结果 + 唤醒调用方)→ 监听型(`auto_relisten`)fd: + `net_listener_settle(QUARANTINE)` + `net_listener_request(REQ_DROP)` 登记即返, + 芯片拆除(FIN 关闭 + 排渣 + 排空事件 + 等 byte0=00H)与重建由 `net_listener_tick` + 按 30ms 节奏执行(与被动超时/断连释放同一台机器),失败交 D10 + 形态④; + 非监听 client fd 无监听可保,仍同步 `net_sock_reset_sync`(600ms 封顶)。 + 端口恢复窗 ~0.4~1s,与被动断连恢复窗(E1 发现 A)同型。 +- **为什么监听型不得同步等待(N-17 定位,论证随结论一并留档)**:旧形态在 + `NET_MSG_DROP_CONN` 处理器内同步跑 `net_sock_reset_sync(QUARANTINE)`(2ms 节奏、 + 400ms 底/600ms 顶,自家注释原记「等待期 netTask 有界占用 ~400-600ms」),netTask + 的 1ms 主循环冻结在同一迭代内:NET 位心跳停摆 + 串行总线整窗停摆(UI 推流/FTP/ + 104 在途消息回执顺延、调用方在其分片/无限等待中不见回执)+ 对时调用方自己睡在 + `net_wait_result` 等回执、期间无分片心跳——五位同窗缺席 → 连续 8 个 100ms 抽样 + 停喂 → 声明 `[WD] task stall`(V5 时代 req=0x37,缺席位恰为 NET/FTP/UI/对时, + 快照只剩 bit0,即日志中的 `mask=0x00000001`),缺口 800ms~1s,距 IWDG 最坏超时 + 1701ms 余量仅 ~0.7s;同场隔离负载(纯 FTP 120s)0 次——因为被动拆链走的本就是 + 异步沉降机(D10「重建必须非阻塞」条令),只有主动 drop 一条路径留在同步原语里。 +- **评估否决的方向——「缩短重建重试(5×2s)」**:该退避链在 + `time_sync_open_listener()` 的 bind/listen 失败路径,全程走 + `time_sync_delay_fed()` ≤50ms 分片喂心跳,且**不在 drop→拆链→重监听的心跳缺口 + 链上**;缩短它不消除本缺口,反而加大对芯片沉降窗的撞车风险(TRAP-NET-09:关闭 + 后需沉降才能重开),故不采。 +- **竞态与信源核对**:① 主路径置 CLOSED 后迟到的 DISCONNECT/TIMEOUT 事件被 + `net_events` 的状态门拦截(只认活会话态),不会把刚登记的 QUARANTINE 降级为 + GRACEFUL;② 零新增读清点——隔离窗内两处本 Socket 读清仍在 `net_listener_tick` + 原位置(§4.10.2),全局寄存器唯一读者仍是 `net_poll`;③ 生产调用者全仓仅 + `time_sync.c`(不消费返回值,accept 轮询天然容忍 WOULDBLOCK 窗)。 - **幂等**(非 ESTABLISHED/CONNECTING 直接返 0);**EOF 路径不得调** (`auto_relisten` 已接管,再调属重复拆)。 -- 芯片物理边界(E-02 台架):`close` **无 RST 语义**——对端配合(收到 FIN 后其内核 - close):`TIME_WAIT` ~150ms → 形态④ relisten(恢复全程 ~500ms);对端内核存活但 - 不 close:芯片停 `FIN_WAIT_2` 长尾,**监听恢复受限于对端 close**,兜底 = 芯片 - TIMEOUT/KeepAlive 拆僵尸 30~90s(E13)——不是应用可加速的路径。 +- 芯片物理边界(E-02 台架,异步收口逐字不变——只移动「等待发生在哪」): + `close` **无 RST 语义**——对端配合(收到 FIN 后其内核 close):`TIME_WAIT` ~150ms + → 形态④ relisten(恢复全程 ~500ms);对端内核存活但不 close:芯片停 + `FIN_WAIT_2` 长尾,**监听恢复受限于对端 close**,兜底 = 芯片 TIMEOUT/KeepAlive 拆 + 僵尸 30~90s(E13)——不是应用可加速的路径。 +- 回归锚点:`TC-NETSOCK-240efg / 321 全组 / 460ef`(登记式、netTask 零占用、 + client fd 旧形态保留);台架判据 = V5 复跑(e02_probe + FTP ≥30min)零 stall, + 见 `docs/测试说明/实机测试实施指南.md` §4。 ### 4.7 KeepAlive(服务器侧) diff --git a/test/host/net_socket/ns_s3_msg.c b/test/host/net_socket/ns_s3_msg.c index 37d0404d..68394dbc 100644 --- a/test/host/net_socket/ns_s3_msg.c +++ b/test/host/net_socket/ns_s3_msg.c @@ -550,8 +550,10 @@ static void tc_240_drop_conn_branches(void) CHECK(g_peers.pending_settle_calls == 1 && g_peers.send_cancel_calls == 1, "TC-NETSOCK-240e both pending tables are ended before dropping the session (with notification)"); - CHECK(g_fxchip.rst_begin_calls == 1, - "TC-NETSOCK-240f teardown uses sock_reset deterministic draining, not close plus blind waiting"); + CHECK(g_peers.listener_settle_calls == 1 && + g_peers.last_settle_level == (int)NET_LISTENER_SETTLE_QUARANTINE && + g_fxchip.rst_begin_calls == 0, + "TC-NETSOCK-240f listener teardown is REGISTERED for the tick machine (QUARANTINE settle + REQ_DROP) - the handler starts no reset machine and never waits (N-17)"); CHECK(g_peers.listener_req_calls == 1 && g_peers.last_req_reason == (int)NET_LISTENER_REQ_DROP, "TC-NETSOCK-240g listener-type fd registers REQ_DROP after the drop (rebuild belongs to the listener layer)"); diff --git a/test/host/net_socket/ns_s4_state.c b/test/host/net_socket/ns_s4_state.c index 2ba1efb4..fd2211a2 100644 --- a/test/host/net_socket/ns_s4_state.c +++ b/test/host/net_socket/ns_s4_state.c @@ -274,7 +274,8 @@ static void tc_320_close_two_paths(void) reset_all(); } -/* TC-NETSOCK-321 drop_conn 用 QUARANTINE(异常释放语义),且不动监听簿记 */ +/* TC-NETSOCK-321 drop_conn:监听型 = 登记式(settle QUARANTINE + REQ_DROP, + * N-17 起 netTask 零占用);非监听 client fd 保留同步排渣;两者都不动监听簿记 */ static void tc_321_drop_conn_settle(void) { int fd; @@ -287,8 +288,11 @@ static void tc_321_drop_conn_settle(void) g_fake.cached_open[fd] = CH395F_SOCKET_OPEN; CHECK(net_drop_conn(fd) == 0, "TC-NETSOCK-321a drop_conn succeeds"); - CHECK(g_fxchip.rst_begin_calls == 1 && g_fx.rst_mode[fd] == 1U, - "TC-NETSOCK-321b session drop uses QUARANTINE with a 400ms floor (TRAP-NET-18 abnormal release, graded against close)"); + CHECK(g_peers.listener_settle_calls == 1 && g_peers.last_settle_fd == fd && + g_peers.last_settle_level == (int)NET_LISTENER_SETTLE_QUARANTINE, + "TC-NETSOCK-321b listener fd: abnormal-release isolation REGISTERED for the tick machine (TRAP-NET-18 QUARANTINE semantics survive, executed by net_listener_tick)"); + CHECK(g_fxchip.rst_begin_calls == 0 && g_ns_error_count == 0, + "TC-NETSOCK-321b2 N-17: the handler starts no synchronous reset and logs nothing - netTask keeps its ~1ms beat"); CHECK(s_net_socks[fd].lost_conn_seen == 0U, "TC-NETSOCK-321c new session start: the missed-event debounce counter is cleared (N-30)"); CHECK(s_net_socks[fd].local_port == 2404U && s_net_socks[fd].auto_relisten == 1U && @@ -300,6 +304,17 @@ static void tc_321_drop_conn_settle(void) CHECK(s_net_socks[fd].readable == 0U && s_net_socks[fd].state == NET_SOCK_STATE_CLOSED, "TC-NETSOCK-321f session bookkeeping closes out: clear readable and move to CLOSED"); reset_all(); + + /* 非监听 client fd:无监听可保,同步排渣形态原样保留(N-17 只改监听型) */ + bring_up(); + fd = mk_established(NET_SOCK_STREAM); + g_fake.cached_open[fd] = CH395F_SOCKET_OPEN; + CHECK(net_drop_conn(fd) == 0, "TC-NETSOCK-321g client-fd drop still succeeds"); + CHECK(g_peers.listener_settle_calls == 0 && g_peers.listener_req_calls == 0, + "TC-NETSOCK-321h client fd registers no settle and no REQ_DROP: nothing to rebuild"); + CHECK(g_fxchip.rst_begin_calls == 1 && g_fx.rst_mode[fd] == 1U, + "TC-NETSOCK-321i client fd keeps the synchronous QUARANTINE close-out (bounded 600ms, legacy shape)"); + reset_all(); } /* TC-NETSOCK-322 close 前必先把挂起结算掉,再动芯片(顺序不可换) */ diff --git a/test/host/net_socket/ns_s5_io.c b/test/host/net_socket/ns_s5_io.c index 8b51b029..ceec34ac 100644 --- a/test/host/net_socket/ns_s5_io.c +++ b/test/host/net_socket/ns_s5_io.c @@ -554,16 +554,28 @@ static void tc_460_reset_sync_timeout_bound(void) "TC-NETSOCK-460d the slot is released even on timeout (a chip state must not jam the local fd table)"); reset_all(); - /* drop_conn 同一条超时线 */ + /* N-17:监听型 fd 的 drop 不再走复位等待线——登记即返;即便芯片永不落回 + * CLOSED 也与本调用无关(排渣与等待归 net_listener_tick 的隔离机) */ bring_up(); fd = mk_established(NET_SOCK_STREAM); s_net_socks[fd].auto_relisten = 1U; g_fake.cached_open[fd] = CH395F_SOCKET_OPEN; g_fx.rst_keep_open[fd] = 1U; t_before = g_ns_tick; + CHECK(net_drop_conn(fd) == 0 && (g_ns_tick - t_before) <= 20U && + g_ns_error_count == 0 && g_peers.listener_settle_calls == 1, + "TC-NETSOCK-460e listener drop returns registered and off the beat budget: no 600ms close-out, no [ERR] line, isolation armed (N-17)"); + reset_all(); + + /* 非监听 client fd 保留旧超时线(同步、600ms 封顶、留痕) */ + bring_up(); + fd = mk_established(NET_SOCK_STREAM); + g_fake.cached_open[fd] = CH395F_SOCKET_OPEN; + g_fx.rst_keep_open[fd] = 1U; + t_before = g_ns_tick; CHECK(net_drop_conn(fd) == 0 && (g_ns_tick - t_before) <= 700U && g_ns_error_count >= 1, - "TC-NETSOCK-460e drop_conn takes the same bounded close-out and logs it too"); + "TC-NETSOCK-460f client-fd drop keeps the same bounded close-out as net_close and logs it too"); reset_all(); }