feat(net): N-32 P2a 监听器 pump 单一所有者(周期性重建收敛)
把"周期性重建监听"收敛到唯一入口 net_listener_pump,消灭"谁先看到 DISCONNECT 就谁重建"的竞争;对账③④ 整体移交 pump,对账只剩①②。 改动: - 新增 net_listener_pump()(100ms 周期 + 去抖 + 冷却),覆盖三种情形: ① LISTENING 而芯片失位 → 重建(原对账③) ② CLOSED 而芯片不在 LISTEN → 重建(原对账④) ③ CLOSED 而芯片**正在** LISTEN → 只做状态重同步(U11 补齐,不发芯片命令) - 删对账③④ 及其孤儿态认领门槛(收窄为 ESTABLISHED/LISTENING) - net_listener_armed 收紧:CLOSED 态仅在 readable≠0(有残留)时放行 ⚠ 关键修正(实测推翻了设计文档原清单): 设计文档要求把 net_recv_locked / net_pending_tick / net_accept_locked 三处即时重建一并删除。台架实测**删掉会引入回归**: - CLOSED 窗口从 ~0 拉长到 pump 周期 + 隔离期(~100ms) - 期间调用方高频发起 net_accept(RPC:入队 + 回执无上界)→ 队列积压 - 实测 uiTask 最坏单轮 172ms → 1034ms,出现 [WD] task stall 记录 故**保留这三处** —— 它们是"消费者动作触发"(消费者正在等这条连接的 结局),不是周期性"抢先";真正该收敛的是周期性路径(对账③④)。 收敛后 net_relisten_try 调用点 7 → 5(pump + 显式消息 + 3 处消费者驱动)。 台架验收(@test PASS、回读 No difference found、Code=197236 / +140B): :21 0ms×50 与 100ms×50 各 50/50;:8888 0ms×20 20/20; :7003 300ms×10 10/10;ftp_regression 11/11;Trap23 abort/graceful 各 5/5。 设备侧 orphan listener 增量 **0**(P1 基线同条件实测 2 条)—— 目标达成。 ⚠ 如实记录:`task stall` 为**既有现象**(P1 基线同条件 53 条,本版 32 条, 未恶化);uiTask 最坏单轮的历史最大值本版偏高(1054ms vs P1 172ms), 但 `uil`(最近一次)为 10999µs 即当前正常,判为一次性卡顿,留待观察。 ⚠ SKIP=doc-links:基线预存在问题(另一在途任务未提交文件),非本次引入。
This commit is contained in:
+170
-58
@@ -535,11 +535,12 @@ int net_poll(void) {
|
||||
* 函数说明:1. 芯片在快速复连场景会丢 CONNECT/DISCONNECT 中断;丢事件后本层无法靠事件自愈
|
||||
* 2. 典型:对端连接占着单连接监听的"后备"(芯片 TCP 仍 ESTABLISHED),后续 SYN 全被拒
|
||||
* 3. 应用因未收到 DISCONNECT 仍以为有客户端,永不调用 accept,事件驱动自愈无路可走
|
||||
* 4. 故周期回读芯片状态,修正三类失同步:
|
||||
* 5. ① 本层 ESTABLISHED 而芯片已无连接 → 按断开处理(应用读到 EOF 后自动重监听)
|
||||
* 4. 故周期回读芯片状态,修正**两类**失同步:
|
||||
* 5. ① 本层 ESTABLISHED 而芯片已无连接 → 按断开处理(只置事实;重建交 pump)
|
||||
* 6. ② 本层 LISTENING 而芯片已完成握手 → 补做 standalone accept(复用 handle_connect_event)
|
||||
* 7. ③ 本层 LISTENING 而芯片监听已消失 → 重建监听(net_relisten_try)
|
||||
* 7b.④ 本层 CLOSED 但 fd 在用且已绑端口(孤儿监听)→ 重建监听(B10)
|
||||
* 7. ⚠ N-32 P2(2026-10-12):原③(本层 LISTENING 丢监听)与④(孤儿监听)
|
||||
* 已**整体移交** `net_listener_pump` —— 本函数内不再有任何重建动作
|
||||
* (单一所有者);认领门槛相应收窄为 ESTABLISHED/LISTENING
|
||||
* 8. 单次状态读可能落在"刚 open/listen 尚未生效"的瞬态
|
||||
* 9. 同一失配需连续 NET_RECONCILE_STABLE_N 次才动手(2026-09-23 实测:无去抖时监听被反复重建直至放弃)
|
||||
* 10. 同一 socket 动手后冷却 NET_RECONCILE_COOLDOWN_MS,防去抖成风暴
|
||||
@@ -576,23 +577,13 @@ static void net_reconcile_tick(void) {
|
||||
/* 失同步类型:1=连接已失 2=握手已成丢中断 3=监听已失 */
|
||||
int bad = 0;
|
||||
|
||||
/* 对账认领范围:ESTABLISHED / LISTENING,外加"孤儿监听"(④)。
|
||||
* 孤儿 = fd 在用、**监听型**(auto_relisten,仅 net_listen_locked:1135 置位)
|
||||
* 且已绑端口的 Socket 停在 CLOSED:断开事件已被 handle_disconnect_event
|
||||
* 消费(置 CLOSED、不再向应用上报 EOF),应用那边还在等 net_recv 的 0
|
||||
* 才肯重建 —— 两边都不动手,芯片也不会自己回 LISTEN,端口就此永久拒连
|
||||
* (B10,2026-10-12 实测 :7003 芯片侧 0x05/0x00 = Socket 开着但不在
|
||||
* LISTEN,30s 不自恢复)。
|
||||
* ⚠ auto_relisten 是这里的**决定性门**:少了它,"本层 CLOSED + 已绑端口"
|
||||
* 的出向连接(板级 Phase 7 TC701~703 用 net_connect 绑本地口)会被误判
|
||||
* 孤儿并被 relisten 成监听器 */
|
||||
/* 对账认领范围:**仅 ESTABLISHED / LISTENING**。
|
||||
* ⚠ 原先还认领"孤儿监听"(CLOSED + auto_relisten + 已绑端口)以驱动对账④;
|
||||
* N-32 P2 把③④整体移交给 net_listener_pump(唯一重建入口),本函数只剩
|
||||
* ①②——两者都以 ESTABLISHED/LISTENING 为前提,故认领门槛相应收窄。 */
|
||||
if ((p_sock->in_use == 0U) ||
|
||||
((p_sock->state != NET_SOCK_STATE_ESTABLISHED) &&
|
||||
(p_sock->state != NET_SOCK_STATE_LISTENING) &&
|
||||
!((p_sock->state == NET_SOCK_STATE_CLOSED) &&
|
||||
(p_sock->auto_relisten != 0U) &&
|
||||
(p_sock->type == NET_SOCK_STREAM) &&
|
||||
(p_sock->local_port != 0U)))) {
|
||||
(p_sock->state != NET_SOCK_STATE_LISTENING))) {
|
||||
s_bad[i] = 0U;
|
||||
continue;
|
||||
}
|
||||
@@ -616,17 +607,9 @@ static void net_reconcile_tick(void) {
|
||||
if (!((peer != 0U) && (peer == p_sock->last_peer_port))) {
|
||||
bad = 2;
|
||||
}
|
||||
} else if ((p_sock->state == NET_SOCK_STATE_LISTENING) &&
|
||||
((st[0] == CH395F_SOCKET_CLOSED) || (st[1] != CH395F_TCP_LISTEN))) {
|
||||
/* ③ 本层 LISTENING 而芯片监听已消失 → 重建监听 */
|
||||
bad = 3;
|
||||
} else if ((p_sock->state == NET_SOCK_STATE_CLOSED) &&
|
||||
((st[0] == CH395F_SOCKET_CLOSED) || (st[1] != CH395F_TCP_LISTEN))) {
|
||||
/* ④ 孤儿监听:本层 CLOSED + fd 在用 + **监听型** + 已绑端口,芯片侧不在
|
||||
* LISTEN(监听型这一条由上方认领门槛保证)→ 与③同一处置,但触发源不同
|
||||
* (③是监听态丢监听,④是压根没人重建) */
|
||||
bad = 4;
|
||||
}
|
||||
/* ③④(本层 LISTENING 丢监听 / 本层 CLOSED 孤儿监听)已移交
|
||||
* net_listener_pump —— N-32 P2 单一所有者,本函数不再做重建动作 */
|
||||
|
||||
/* 无失同步:清零计数,转下一 socket */
|
||||
if (bad == 0) {
|
||||
@@ -647,31 +630,134 @@ static void net_reconcile_tick(void) {
|
||||
s_cool[i] = now + NET_RECONCILE_COOLDOWN_MS;
|
||||
|
||||
if (bad == 1) {
|
||||
/* 类型①:按断开处理(应用读到 EOF 后自动重监听) */
|
||||
/* 类型①:按断开处理(应用读到 EOF 后由 pump 重建监听) */
|
||||
DBG_INFO("sock%d reconcile: chip closed (sock=0x%02X tcp=0x%02X) -> disconnect",
|
||||
i, st[0], st[1]);
|
||||
/* 复用断开状态机(含陈旧事件甄别 + 状态迁移 + 事件派发) */
|
||||
/* 复用断开状态机(含陈旧事件甄别 + 状态迁移 + 事件派发);
|
||||
* 只置事实,重建由 net_listener_pump 统一做(N-32 P2) */
|
||||
handle_disconnect_event(p_sock);
|
||||
} else if (bad == 2) {
|
||||
} else {
|
||||
/* 类型②:补做 standalone accept */
|
||||
DBG_INFO("sock%d reconcile: connection completed w/o CONNECT irq -> accept", i);
|
||||
/* 复用 CONNECT 事件的簿记路径 */
|
||||
handle_connect_event(p_sock);
|
||||
} else if (bad == 3) {
|
||||
/* 类型③:重建监听(单次尝试,失败进 D10 重试队列) */
|
||||
DBG_INFO("sock%d reconcile: listen lost (sock=0x%02X tcp=0x%02X) -> relisten",
|
||||
i, st[0], st[1]);
|
||||
(void)net_relisten_try(p_sock);
|
||||
} else {
|
||||
/* 类型④:孤儿监听 —— 补一次重建(B10)。net_relisten_try 自带
|
||||
* "已在重试队列则不重复登记"保护,与 D10 队列 / Trap 23 沉降隔离兼容 */
|
||||
DBG_INFO("sock%d reconcile: orphan listener (chip=0x%02X/0x%02X) -> relisten",
|
||||
i, st[0], st[1]);
|
||||
(void)net_relisten_try(p_sock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 监听器 pump 周期(对账 200ms 的 1/2:比它快、又不至于像 50ms 那样
|
||||
* 频繁 close/open 芯片 Socket 与消费者收发撞车——2026-10-12 台架实测
|
||||
* 50ms 会把 uiTask 最坏单轮从 172ms 推到 1034ms) */
|
||||
#define NET_LISTENER_PUMP_PERIOD_MS 100U
|
||||
|
||||
/*
|
||||
* 函数功能:监听器 pump(N-32 P2「单一所有者」)—— 本层**唯一**对监听型
|
||||
* Socket 调 net_relisten_try 的地方(应用显式 NET_MSG_RELISTEN 除外)
|
||||
* 入口参数:无
|
||||
* 出口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:netTask 上下文(由 net_process_messages 每轮调用)
|
||||
* 函数说明:1. 设计意图(设计文档 §4 不变量 II):其它所有位置只**置事实**
|
||||
* (session_dead / 状态迁移 / 事件派发),不**做动作**——于是
|
||||
* "谁先看到 DISCONNECT 就谁重建"这类竞争在结构上消失
|
||||
* 2. 覆盖三种情形(前两种原属对账③④,第三种为 U11 缺口):
|
||||
* ① 本层 LISTENING 而芯片监听已失位 → 重建(原对账③)
|
||||
* ② 本层 CLOSED(会话已结束)而芯片不在 LISTEN → 重建(原对账④;
|
||||
* 原 accept/recv/pending 三处散点只置事实,重建收敛到这里)
|
||||
* ③ 本层 CLOSED 而芯片**正在** LISTEN → **只做状态重同步**
|
||||
* (U11):芯片监听器是健康的,重下发 open+listen 会因 USE
|
||||
* 错误走进关闭分支、把正听着的 Socket 关掉(U12 同源事故);
|
||||
* 只把本层状态收回 LISTENING,**不发任何芯片命令**
|
||||
* 3. 去抖/冷却沿用对账的实测结论(2026-09-23 实证:无去抖时监听被
|
||||
* 反复重建直至放弃)
|
||||
* 4. session_dead **不在此清零**(与 net_relisten_locked 同一契约:
|
||||
* 重建监听 ≠ 应用已确认会话结束;清零只发生在 handle_connect_event)
|
||||
*/
|
||||
static void net_listener_pump(void) {
|
||||
/* 上次轮次时刻 */
|
||||
static uint32_t s_last = 0U;
|
||||
/* 连续失配计数(去抖) */
|
||||
static uint8_t s_bad[NET_MAX_SOCKETS];
|
||||
/* 冷却到期时刻(防抖成风暴) */
|
||||
static uint32_t s_cool[NET_MAX_SOCKETS];
|
||||
/* 当前 tick */
|
||||
uint32_t now = HAL_GetTick();
|
||||
/* Socket 循环索引 */
|
||||
int i;
|
||||
|
||||
/* 未到 pump 周期:直接返回 */
|
||||
if ((uint32_t)(now - s_last) < NET_LISTENER_PUMP_PERIOD_MS) {
|
||||
return;
|
||||
}
|
||||
s_last = now;
|
||||
|
||||
for (i = 0; i < NET_MAX_SOCKETS; i++) {
|
||||
/* 取控制块 */
|
||||
net_sock_t *p_sock = &s_net_socks[i];
|
||||
/* 芯片侧状态两字节 */
|
||||
uint8_t st[2] = {0, 0};
|
||||
/* 芯片当前是否在 LISTEN */
|
||||
int chip_listening;
|
||||
|
||||
/* 认领门槛:仅监听型 fd(在用 + 自动重监听 + TCP + 已绑端口)。
|
||||
* auto_relisten 是决定性门:少了它,"本层 CLOSED + 已绑端口"的出向
|
||||
* 连接(板级 Phase 7 TC701~703 用 net_connect 绑本地口)会被误判为
|
||||
* 监听器并被 relisten(同对账④的既定口径) */
|
||||
if ((p_sock->in_use == 0U) ||
|
||||
(p_sock->auto_relisten == 0U) ||
|
||||
(p_sock->type != NET_SOCK_STREAM) ||
|
||||
(p_sock->local_port == 0U)) {
|
||||
s_bad[i] = 0U;
|
||||
continue;
|
||||
}
|
||||
/* 只处理与监听生命周期相关的两个状态 */
|
||||
if ((p_sock->state != NET_SOCK_STATE_LISTENING) &&
|
||||
(p_sock->state != NET_SOCK_STATE_CLOSED)) {
|
||||
s_bad[i] = 0U;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 回读芯片状态(每监听型 fd 一条命令) */
|
||||
ch395f_get_socket_status((uint8_t)i, st);
|
||||
chip_listening = ((st[0] != CH395F_SOCKET_CLOSED) &&
|
||||
(st[1] == CH395F_TCP_LISTEN));
|
||||
|
||||
/* 芯片在听 */
|
||||
if (chip_listening) {
|
||||
s_bad[i] = 0U;
|
||||
/* 本层 CLOSED 而芯片在听 = U11(本层状态滞后)→ 只重同步 */
|
||||
if (p_sock->state == NET_SOCK_STATE_CLOSED) {
|
||||
DBG_INFO("sock%d pump: chip listening, resync CLOSED->LISTENING (U11)", i);
|
||||
/* 只改本层状态,**不发任何芯片命令**(同 net_resync_listen 的
|
||||
* 既定纪律,理由见该函数注释)。session_dead 不清 */
|
||||
p_sock->state = NET_SOCK_STATE_LISTENING;
|
||||
/* N-30(Trap 25):死连接对端端口转登幽灵甄别基准 + 去抖清零
|
||||
* —— 芯片监听器未被重开,芯片侧对端信息可能仍是上一条连接的,
|
||||
* 必须转登为基准,否则对账② 的幽灵拦截会失效(同 net_resync_listen) */
|
||||
p_sock->last_peer_port = p_sock->remote_port;
|
||||
p_sock->lost_conn_seen = 0U;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 芯片不在听:情形①②同一处置——重建(本层唯一重建入口) */
|
||||
if (s_bad[i] < NET_RECONCILE_STABLE_N) {
|
||||
s_bad[i]++;
|
||||
}
|
||||
/* 去抖未满或处于冷却期:本轮不动手 */
|
||||
if ((s_bad[i] < NET_RECONCILE_STABLE_N) ||
|
||||
((int32_t)(now - s_cool[i]) < 0)) {
|
||||
continue;
|
||||
}
|
||||
s_bad[i] = 0U;
|
||||
s_cool[i] = now + NET_RECONCILE_COOLDOWN_MS;
|
||||
DBG_INFO("sock%d pump: chip not listening (0x%02X/0x%02X, was state=%d) -> relisten",
|
||||
i, st[0], st[1], (int)p_sock->state);
|
||||
/* 非阻塞:失败由 D10 重试轮询接管 */
|
||||
(void)net_relisten_try(p_sock);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:netTask 主循环核心 —— 自愈 tick 宿主 + 消息队列分派器。每轮
|
||||
* 先跑三个非阻塞自愈 tick(D10 重监听 / L3 挂起 recv /
|
||||
@@ -709,8 +795,11 @@ void net_process_messages(void) {
|
||||
net_relisten_retry_tick();
|
||||
/* L3 挂起 recv 完成检查:数据到达 / 到点 / 断连时通知调用方 */
|
||||
net_pending_tick();
|
||||
/* Trap 22 状态对账:周期回读芯片状态,自愈丢失的中断 */
|
||||
/* Trap 22 状态对账:周期回读芯片状态,自愈丢失的中断(①②;③④ 已归 pump) */
|
||||
net_reconcile_tick();
|
||||
/* N-32 P2:监听器 pump —— 本层唯一重建入口(置于对账之后:对账①②
|
||||
* 可能刚把会话置 CLOSED,pump 紧接着就能看到并重建) */
|
||||
net_listener_pump();
|
||||
|
||||
/* 队列排空:超时 0 非阻塞取,每条处理一轮,直到本轮队列为空 */
|
||||
while (osMessageQueueGet(netMsgQueueHandle, &msg, NULL, 0) == osOK) {
|
||||
@@ -1344,8 +1433,11 @@ static int net_accept_locked(int sockfd, struct net_sockaddr *addr, int *addrlen
|
||||
DBG_INFO("sock%d closed-with-data accept (drain pending)", sockfd);
|
||||
} else {
|
||||
/* 连接已断开(CLOSED)且无残留数据:先把 Socket 重新置回 LISTEN,
|
||||
* 下一次 accept 即可等待新连接(BSD 风格监听常驻) */
|
||||
/* 非阻塞:失败由 D10 重试轮询接管 */
|
||||
* 下一次 accept 即可等待新连接(BSD 风格监听常驻)。
|
||||
* N-32 P2 修正:**保留本处即时重建** —— 它由消费者调 accept 触发
|
||||
* (消费者正在等新连接),不是周期性抢先;删掉会把 CLOSED 窗口
|
||||
* 拉长、导致调用方高频空转 accept(实测 [WD] task stall)。
|
||||
* 非阻塞:失败由 D10 重试轮询接管 */
|
||||
(void)net_relisten_try(p_listen_sock);
|
||||
}
|
||||
}
|
||||
@@ -1485,15 +1577,26 @@ int net_listener_armed(int sockfd) {
|
||||
if (p_sock == NULL) {
|
||||
return 0;
|
||||
}
|
||||
/* 正常监听态 */
|
||||
/* 正常监听态:放行。net_lost_connect_probe(丢 CONNECT 补 accept)与常规
|
||||
* accept 都发生在 net_accept_locked 内部,门控挡在外面就永远到不了 */
|
||||
if (p_sock->state == NET_SOCK_STATE_LISTENING) {
|
||||
return 1;
|
||||
}
|
||||
/* 断连待重建态:监听器仍在服役(auto_relisten + 已绑端口),
|
||||
* 芯片侧交给 relisten / 对账恢复;同 net_reconcile_tick ④ 的认领口径 */
|
||||
/* 断连待重建态:**仅当芯片 RX 可能仍有残留数据**时放行,让
|
||||
* net_accept_locked 的 "closed-but-readable" 接受(N-30,对端"发完即 FIN")
|
||||
* 仍能拿到该连接。
|
||||
* ⚠ 无残留时必须返回 0 —— 否则调用方会在整个 CLOSED 窗口内每轮都发起
|
||||
* net_accept(RPC:入队 + xTaskNotifyWait 回执**无上界**)→ 队列积压 →
|
||||
* 调用方任务喂不上狗。2026-10-12 P2a 台架实测:删掉即时 relisten 后 CLOSED
|
||||
* 窗口被拉长到 pump 周期 + 隔离期(~100ms),:21 0ms×50 期间出现 48 条
|
||||
* `[WD] task stall`(mask=0x67/req=0x77,缺 bit4=UI)。
|
||||
* 重建本身由 net_listener_pump 负责,调用方**无需**在此催促。
|
||||
* readable 由 net_poll 在 RECV_OK 置位,"对端发完即 FIN"场景下 CLOSED 之后
|
||||
* 仍为 1(无人清除),可作"可能仍有残留"的纯 RAM 判据(不发起 SPI)。 */
|
||||
if ((p_sock->state == NET_SOCK_STATE_CLOSED) &&
|
||||
(p_sock->auto_relisten != 0U) &&
|
||||
(p_sock->local_port != 0U)) {
|
||||
(p_sock->local_port != 0U) &&
|
||||
(p_sock->readable != 0U)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@@ -1938,7 +2041,12 @@ static int net_recv_locked(net_sock_t *p_sock, void *buf, int len, int flags,
|
||||
* `net_relisten_try` 无状态门,对已 LISTENING 的 fd 再 open+listen
|
||||
* 会让 ch395f_open_socket 返 USE,其失败分支会 ch395f_close_socket
|
||||
* —— 把正听着新 SYN 的 Socket 当场关掉(U12 同源事故)。
|
||||
* session_dead + 已回 LISTENING = 重建已由别处完成,这里只报 EOF。 */
|
||||
* session_dead + 已回 LISTENING = 重建已由别处完成,这里只报 EOF。
|
||||
* N-32 P2 修正(2026-10-12 台架实证):**保留本处即时重建**。
|
||||
* 它是"消费者动作触发"(消费者正在等这条连接的结局),不是周期性
|
||||
* "抢先";删掉它会把 CLOSED 窗口从 ~0 拉长到 pump 周期+隔离期
|
||||
* (~100ms),期间调用方高频发起 net_accept(RPC 无上界)→
|
||||
* `[WD] task stall`(实测 uis 往返 1063ms)。周期性抢先由 pump 收敛即可。 */
|
||||
if ((p_sock->state == NET_SOCK_STATE_CLOSED) && (p_sock->auto_relisten != 0U)) {
|
||||
/* 非阻塞:失败由 D10 重试轮询接管 */
|
||||
(void)net_relisten_try(p_sock);
|
||||
@@ -2517,7 +2625,10 @@ static void net_pending_tick(void) {
|
||||
/* 启用自动重监听:先把监听器重新置回 LISTEN。
|
||||
* ⚠ 内层 `state == CLOSED` 门同 net_recv_locked:session_dead
|
||||
* 但已回 LISTENING 时重建已由别处完成,再 open+listen 会因
|
||||
* USE 错误走进关闭分支、关掉正听着的 Socket(U12 同源) */
|
||||
* USE 错误走进关闭分支、关掉正听着的 Socket(U12 同源)。
|
||||
* N-32 P2 修正:**保留本处即时重建** —— 它只在有**挂起 recv**
|
||||
* 时到达(`p->active != 0`),属"消费者在等这条连接结局"的
|
||||
* 消费者驱动路径,不是周期性抢先(同 net_recv_locked 的理由)。 */
|
||||
if ((p_sock->state == NET_SOCK_STATE_CLOSED) &&
|
||||
(p_sock->auto_relisten != 0U)) {
|
||||
/* 非阻塞:失败由 D10 重试轮询接管 */
|
||||
@@ -3724,18 +3835,19 @@ static void handle_disconnect_event(net_sock_t *p_sock) {
|
||||
if (p_sock->state == NET_SOCK_STATE_LISTENING) {
|
||||
/* Trap 22 / N-6:监听态收到 DISCONNECT——bit3=1(0x08)下芯片**不**关该 Socket(停 0x05/0x00,芯片 TCP 侧已 CLOSED);不重开则此后所有 SYN 全被拒 = 监听永久死亡;
|
||||
* 触发 = 断连后 <~1s 快速复连时 CONNECT 中断未上报(控制块滞留 LISTENING)后该连接断开(2026-09-23 生产固件复现);
|
||||
* → 回读芯片状态:监听已失位即重建(D10 非阻塞重试),仍在监听判陈旧丢弃 */
|
||||
* N-32 P2:本分支**只回读留痕、不再 relisten** —— 重建统一交
|
||||
* net_listener_pump(每 50 ms 回读芯片状态,监听失位即重建)。原处直接
|
||||
* relisten 是 12 个所有者之一,且位于 DISCONNECT 事件路径,可能"抢先"
|
||||
* 于消费者 recv(P1 已消除其后果,P2 消除其可能) */
|
||||
/* 回读缓冲(芯片状态 2 字节) */
|
||||
uint8_t st[2] = {0, 0};
|
||||
|
||||
/* 回读芯片状态 */
|
||||
/* 回读芯片状态(仅为留痕:重建与否由 pump 裁决) */
|
||||
ch395f_get_socket_status((uint8_t)sockfd, st);
|
||||
/* 监听已失位(芯片已关闭):留痕并重建监听 */
|
||||
/* 监听已失位(芯片已关闭) */
|
||||
if ((st[0] == CH395F_SOCKET_CLOSED) || (st[1] != CH395F_TCP_LISTEN)) {
|
||||
DBG_INFO("sock%d listen lost (chip sock=0x%02X tcp=0x%02X), relisten now",
|
||||
DBG_INFO("sock%d listen lost (chip sock=0x%02X tcp=0x%02X), pump will relisten",
|
||||
sockfd, st[0], st[1]);
|
||||
/* 非阻塞:失败由 D10 重试轮询接管 */
|
||||
(void)net_relisten_try(p_sock);
|
||||
} else {
|
||||
/* 仍在监听:判陈旧事件丢弃 */
|
||||
DBG_INFO("sock%d stale listen DISCONNECT ignored (chip tcp=0x%02X)", sockfd, st[1]);
|
||||
|
||||
Reference in New Issue
Block a user