refactor(net)!: N34 阶段1 API 面收敛(对账⑤ + 删 3 个手动自愈 API)

BREAKING CHANGE: 删除公开 API net_relisten / net_resync_listen /
net_can_accept 与消息类型 NET_MSG_RESYNC_LISTEN / NET_MSG_RELISTEN。
生产调用者为零(仅看门狗文档与 iec104 门控),应用侧无迁移成本;
如有外部消费者须改用对账/pump 自动机制(见下)。

1. 对账①扩展为①②⑤(F-NET-2 缺口,先补缺口再删 API)
   net_reconcile_tick 新增 bad=5:本层 ESTABLISHED 且芯片 0x05/0x01
   (Socket OPEN + TCP LISTEN,静默回收回监听、不发 DISCONNECT)
   → RESYNC 语义只修本层(state→LISTENING + last_peer_port 转登 +
   lost_conn_seen 清零 + 取消挂起 recv),**不发任何芯片命令**
   (纪律同 pump③ U11,run8 实证重下发会 170s 永拒)。
   原判据①要求 TCP==CLOSED、②要求本层 LISTENING,均不命中此形态
   ——此前生产无人认领(仅手动 API 能修)。
2. 删 net_relisten(net_socket.c 函数 + 消息分派 + 头文件声明)
   footgun:对健康监听器无条件 open→USE→close 自毁 ~1s(原注释自认);
   与 pump(100ms)/D10(30ms) 完全重叠。
3. 删 net_resync_listen(同上;语义并入第 1 条)
4. 删 net_can_accept(net_socket.c + net_socket.h + sim 三处)
   只认 ESTABLISHED 的旧门控把"监听器在服役、待 CONNECT"窗口全挡掉,
   :2404 丢 CONNECT 探针因此从不运行;iec104 门控迁 net_listener_armed。
5. timeSync :8888 会话收尾不再 net_close(同 fd 续 accept,监听跨会话
   常驻;原 close→重建每会话一个 Trap 09 沉降窗)。server 脚本注释同步
   新契约(PC 侧 2s 宽限后自行关闭)。
6. lftpd 应用层阻塞重试退役:lftpd_inet_listen 5 轮(500ms/2000ms,最坏
   10s 阻塞 ftpTask) → 单次 + 失败即 425;cmd_pasv 3×200ms 同删。
   就绪屏障已消除 Trap 09 启动形态,客户端自带传输重试语义。

台架实证修正(本提交内二次修复,务必留痕):
- net_listener_armed 补齐 **ESTABLISHED 放行**。首版迁移只放行
  LISTENING/CLOSED+残留,漏了 ESTABLISHED=单连接模型的"待 accept"电平
  → iec104 200ms 任务周期抢不过对账② 的 ~400ms 去抖,对账②先 standalone
  accept 转 ESTABLISHED 后 armed=0 → 应用整场不 accept,18/18 检查全 FAIL
  (设备日志证据:sock1 standalone accept 后无 i104 accepted,直至对端 FIN
  才由 closed-with-data 救回)。补放行后 18/18 PASS,且耗时 127s→8.3s。
  该态 accept 必返 fd,无 RPC 风暴风险。
- 期间顺带验证 M3 无辜:sock1 suspect DISCONNECT on live conn 命中后
  defer→对账①→closed-with-data accept 把未 accept 的 RX 数据救回,
  正是 M3 设计意图(资产门保护)。

验证(全绿):@build 0/0(Code=209580;含 CH395F 套件+Phase13 全量重编
0/0 的编译验证);@test PASS(回读无差异);iec104 18/18(8.3s);
n30 0/50;:21 @0ms 50/50 p50=512;:7003 50/50 p50=517;ftp_regression
11/11;Trap23 RST 5/5 + FIN 5/5;time_sync :8888 连续推送正常(RTC 更新、
无 [ERR])。文档同步:BSD 指南(§3.3/3.6/10.1/10.2/12 + 变更记录)、
函数调用关系、监听生命周期设计说明、IEC104 上行设计说明、模拟器差距
清单、链路延迟测试说明。

门禁说明:doc-links 工作树 junction 伪影按既定口径 SKIP;mem-budget
分支 .map 漂移同前;cppcheck/coding-standard 手跑 Passed。
This commit is contained in:
2026-10-13 06:44:26 +08:00
parent 4c0a0af9f8
commit b8c6a3dab0
16 changed files with 196 additions and 374 deletions
+3 -8
View File
@@ -462,19 +462,14 @@ static int cmd_pass(lftpd_client_t *client, const char *arg)
static int cmd_pasv(lftpd_client_t *client, const char *arg)
{
int listener_socket = -1;
int retry;
if (client->data_socket >= 0) {
lftpd_inet_close(client->data_socket);
client->data_socket = -1;
}
for (retry = 0; retry < 3; retry++) {
listener_socket = lftpd_inet_listen(0);
if (listener_socket >= 0) break;
vTaskDelay(pdMS_TO_TICKS(200));
}
/* N34 阶段1-6:原 3×200ms 应用层重试已删(lftpd_inet_listen 单次化)——
* 失败直接 425,FTP 客户端自带传输重试语义;服务端不再叠阻塞重试 */
int listener_socket = lftpd_inet_listen(0);
if (listener_socket < 0) {
send_simple_response(client->socket, 425, STATUS_425);
return -1;
+27 -31
View File
@@ -34,39 +34,35 @@ void lftpd_task_wd_beat(void)
int lftpd_inet_listen(int port)
{
int retry;
for (retry = 0; retry < 5; retry++) {
lftpd_task_wd_beat(); /* 看门狗:每轮喂心跳(重试间隔 500ms 在 800ms 容差内) */
int s = net_socket(NET_AF_INET, NET_SOCK_STREAM);
if (s < 0) {
if (retry == 0) DBG_ERROR("create socket");
vTaskDelay(pdMS_TO_TICKS(500));
continue;
}
struct net_sockaddr_in addr;
addr.sin_family = NET_AF_INET;
addr.sin_port = net_htons((uint16_t)port);
addr.sin_addr.s_addr = 0;
if (net_bind(s, (struct net_sockaddr *)&addr, sizeof(addr)) < 0) {
DBG_ERROR("bind port %d", port);
net_close(s);
return -1;
}
if (net_listen(s) >= 0) {
return s;
}
net_close(s);
if (retry < 4) {
vTaskDelay(pdMS_TO_TICKS(2000));
}
/* N34 阶段1-6:应用层阻塞重试退役(原 5 轮 500ms/2000ms,最坏 10s 阻塞
* ftpTask)——Trap 09 的启动形态已由就绪屏障消除(FTP_READY 等 NET_READY,
* lftpd_start 外层本就有监听失败重建循环);芯片忙窗失败属罕见,PASV 数据
* 监听交给 FTP 客户端自身的重试语义(主流 FTP 服务器 passive listen 失败
* 亦直接 425,不在服务端叠应用层阻塞重试) */
lftpd_task_wd_beat(); /* 看门狗:入口喂心跳 */
int s = net_socket(NET_AF_INET, NET_SOCK_STREAM);
if (s < 0) {
DBG_ERROR("create socket");
return -1;
}
DBG_ERROR("listen failed after 5 retries (total 10s wait)");
struct net_sockaddr_in addr;
addr.sin_family = NET_AF_INET;
addr.sin_port = net_htons((uint16_t)port);
addr.sin_addr.s_addr = 0;
if (net_bind(s, (struct net_sockaddr *)&addr, sizeof(addr)) < 0) {
DBG_ERROR("bind port %d", port);
net_close(s);
return -1;
}
if (net_listen(s) >= 0) {
return s;
}
DBG_ERROR("listen port %d failed", port);
net_close(s);
return -1;
}
+54 -170
View File
@@ -542,9 +542,11 @@ int net_poll(void) {
* 函数说明:1. 芯片在快速复连场景会丢 CONNECT/DISCONNECT 中断;丢事件后本层无法靠事件自愈
* 2. 典型:对端连接占着单连接监听的"后备"(芯片 TCP 仍 ESTABLISHED),后续 SYN 全被拒
* 3. 应用因未收到 DISCONNECT 仍以为有客户端,永不调用 accept,事件驱动自愈无路可走
* 4. 故周期回读芯片状态,修正**两类**失同步:
* 4. 故周期回读芯片状态,修正**三类**失同步:
* 5. ① 本层 ESTABLISHED 而芯片已无连接 → 按断开处理(只置事实;重建交 pump)
* 6. ② 本层 LISTENING 而芯片已完成握手 → 补做 standalone accept(复用 handle_connect_event)
* 6b.⑤(N34 阶段1,F-NET-2)本层 ESTABLISHED 而芯片已静默回收回 LISTEN
* (0x05/0x01 无 DISCONNECT)→ RESYNC 只修本层,纪律同 pump③ U11
* 7. ⚠ N-32 P2(2026-10-12):原③(本层 LISTENING 丢监听)与④(孤儿监听)
* 已**整体移交** `net_listener_pump` —— 本函数内不再有任何重建动作
* (单一所有者);认领门槛相应收窄为 ESTABLISHED/LISTENING
@@ -581,7 +583,8 @@ static void net_reconcile_tick(void) {
net_sock_t *p_sock = &s_net_socks[i];
/* 芯片侧状态两字节:[0]=socket 状态,[1]=TCP 状态 */
uint8_t st[2] = {0, 0};
/* 失同步类型:1=连接已失 2=握手已成丢中断 3=监听已失 */
/* 失同步类型:1=连接已失 2=握手已成丢中断 5=芯片静默回收回监听
*(原 3/4=监听失位已移交 pump,编号不复用以避与历史口径混淆) */
int bad = 0;
/* 对账认领范围:**仅 ESTABLISHED / LISTENING**。
@@ -614,6 +617,15 @@ 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_ESTABLISHED) &&
(st[0] != CH395F_SOCKET_CLOSED) &&
(st[1] == CH395F_TCP_LISTEN)) {
/* ⑤(F-NET-2 缺口,N34 阶段1):芯片静默回收——本层 ESTABLISHED
* 而芯片已自行把 Socket 收回 LISTEN(0x05/0x01,**不发 DISCONNECT
* 中断**,run8 台架实证)。判据①要求 TCP==CLOSED 不命中此形态、
* 判据②要求本层 LISTENING 也不命中——此前生产无人认领(原仅手动
* API net_resync_listen 能修且生产零调用)。→ bad=5 RESYNC 语义 */
bad = 5;
}
/* ③④(本层 LISTENING 丢监听 / 本层 CLOSED 孤儿监听)已移交
* net_listener_pump —— N-32 P2 单一所有者,本函数不再做重建动作 */
@@ -643,6 +655,20 @@ static void net_reconcile_tick(void) {
/* 复用断开状态机(含陈旧事件甄别 + 状态迁移 + 事件派发);
* 只置事实,重建由 net_listener_pump 统一做(N-32 P2) */
handle_disconnect_event(p_sock);
} else if (bad == 5) {
/* 类型⑤:静默回收 → RESYNC 语义(原 NET_MSG_RESYNC_LISTEN 分派体
* 原样并入,N34 阶段1;纪律同 pump③ U11:**不发任何芯片命令**——
* 重下发 close/重监听会摧毁芯片侧健康监听器 → 端口永拒,
* run8 实证 170s 拒绝) */
DBG_INFO("sock%d reconcile: chip re-listened w/o DISCONNECT (0x%02X/0x%02X) -> resync",
i, st[0], st[1]);
/* 仅修 MCU 侧;新连接的 CONNECT 中断会把两侧重新同步 */
p_sock->state = NET_SOCK_STATE_LISTENING;
/* N-30(Trap 25):死连接对端端口转登幽灵甄别基准 + 去抖清零 */
p_sock->last_peer_port = p_sock->remote_port;
p_sock->lost_conn_seen = 0U;
/* 取消本 fd 在死连接上的挂起 recv(必通知,防调用方挂死) */
net_pending_cancel(i, NET_ERR_CONNRESET);
} else {
/* 类型②:补做 standalone accept */
DBG_INFO("sock%d reconcile: connection completed w/o CONNECT irq -> accept", i);
@@ -783,47 +809,10 @@ void net_process_messages(void) {
msg.result = net_accept_locked(msg.sockfd, p_addr, p_addrlen);
break;
}
case NET_MSG_RESYNC_LISTEN: {
/* 取控制块;NULL = fd 越界或已关闭 */
net_sock_t *p_sock = net_get_sock(msg.sockfd);
if (p_sock == NULL) {
/* 描述符无效 → BADF,结果 -1 */
net_set_errno(NET_ERR_BADF);
msg.result = -1;
} else if (p_sock->state != NET_SOCK_STATE_ESTABLISHED) {
/* 非 ESTABLISHED(LISTENING/CLOSED 等):两侧已一致,无需动作 */
msg.result = 0;
} else {
/* 陈旧 ESTABLISHED:芯片已静默回收连接(无 DISCONNECT 中断,
* 2026-09-22 台架实证)→ 仅修 MCU 侧状态;严禁发芯片
* close/重监听命令(会摧毁芯片侧仍健康的监听器 → 端口永拒,
* run8 实证 170s 拒绝);新连接的 CONNECT 中断会把两侧重新同步 */
p_sock->state = NET_SOCK_STATE_LISTENING;
/* N-30(Trap 25):死连接对端端口转登幽灵甄别基准 + 去抖清零 */
p_sock->last_peer_port = p_sock->remote_port;
p_sock->lost_conn_seen = 0U;
/* 取消本 fd 在死连接上的挂起 recv(必通知,防调用方挂死) */
net_pending_cancel(msg.sockfd, NET_ERR_CONNRESET);
/* 仅修正 MCU 侧状态,不发芯片命令,直接返回成功 */
msg.result = 0;
}
break;
}
case NET_MSG_RELISTEN: {
/* 描述符有效性检查(NULL = fd 越界或已关闭)→ 无效即 BADF;
* 有效则交 net_listener_try(它内部自行取控制块,本处不再持有指针,
* N-32 §10 入口收敛) */
if (net_get_sock(msg.sockfd) == NULL) {
/* 描述符无效 → BADF,结果 -1 */
net_set_errno(NET_ERR_BADF);
msg.result = -1;
} else {
/* 无条件芯片侧重监听:单次尝试,失败登记非阻塞重试
* (芯片已在监听 → 下一轮到点再来;两种陈旧场景都收敛到"芯片监听器在位") */
msg.result = net_listener_try(msg.sockfd);
}
break;
}
/* N34 阶段1(F-NET-3):NET_MSG_RESYNC_LISTEN / NET_MSG_RELISTEN 两个
* 手动自愈消息已删——RESYNC 语义并入对账⑤(net_reconcile_tick),
* RELISTEN 与 pump/D10 完全重叠且对健康监听器是 footgun;
* 生产调用者本就为零(仅 test/board Phase 13,已改由自动机制接管) */
default:
/* 未知消息类型(枚举闭合,正常不应触发):置通用错误码与结果 -1,
* 保证通知值恒为本条消息的确定结果(无上一条残留) */
@@ -1429,26 +1418,10 @@ int net_accept(int sockfd, struct net_sockaddr *addr, int *addrlen) {
return msg.result;
}
/*
* 函数功能:查询监听 Socket 是否有可接受的连接(纯 RAM 读,不发起 RPC)
* 入口参数:sockfd - 监听 Socket 描述符 int
* 出口参数:无
* 返回值:1 - 有连接可接受;0 - 无(含描述符无效) int
* 限定条件:任意任务上下文;net_init() 已调用
* 函数说明:1. 判据与 net_accept_locked() 一致(单连接模型:监听 Socket 自身在
* CONNECT 后转 ESTABLISHED),故"门控放行"与"accept 会成功"严格等价
* 2. 只读控制块,不入队、不等通知 → 调用方(uiTask 等)不被 netTask 拖住
* 3. 丢 CONNECT 中断由 net_reconcile_tick() 对账自愈,本函数不做自愈
*/
int net_can_accept(int sockfd) {
/* Socket 控制块(纯 RAM 读,可能为 NULL = 描述符越界或未分配) */
const net_sock_t *p_sock = net_get_sock(sockfd);
if (p_sock == NULL) {
return 0;
}
return (p_sock->state == NET_SOCK_STATE_ESTABLISHED) ? 1 : 0;
}
/* N34 阶段1(F-NET-4):net_can_accept 已删除——它只认 ESTABLISHED,作为 accept
* 门控会把"监听器在服役但本轮还没 CONNECT"的窗口全挡掉(:2404 丢 CONNECT 探针
* 因此从不运行,与已修的 :7003 探针失效同型);唯一生产调用者 iec104 已迁
* net_listener_armed。net_can_read(RECV 门控)不受影响、保留。 */
/*
* 函数功能:查询监听 Socket 是否处于"应该尝试 accept"的就绪态(纯 RAM 读,不发起 RPC)
@@ -1457,8 +1430,9 @@ int net_can_accept(int sockfd) {
* 返回值:1 - 就绪(LISTENING,或 CLOSED + auto_relisten = 断连待重建态);
* 0 - 未就绪(fd 未分配 / 非监听型 / 正处隔离期以外的不就绪态) int
* 限定条件:任意任务上下文;net_init() 已调用
* 函数说明:1. N-32(P1 不变量 II):取代 net_can_accept()(要求 ESTABLISHED)作为
* ui_net_sink 的 accept 门控。原门控下 :7003 永不进 net_accept →
* 函数说明:1. N-32(P1 不变量 II):取代已退役的 net_can_accept()(要求
* ESTABLISHED,N34 阶段1 删除)作为监听 accept 门控。原门控下
* :7003 永不进 net_accept →
* net_lost_connect_probe(丢 CONNECT 中断补 accept)对该端口从不运行
* (设计文档 §2.3 末"探针失效")
* 2. 为什么放行 LISTENING:探针与"closed-but-readable 接受"(:1338)
@@ -1469,7 +1443,7 @@ int net_can_accept(int sockfd) {
* 可能已被芯片接住(对账②)→ 放行让 net_accept_locked 的状态门
* 自行裁决(含 RX 有数据的 drain_pending 接受)
* 4. 只读控制块,不入队、不等通知 → uiTask 不被 netTask 拖住
* 5. 与 net_can_accept 语义差异是**有意的**:本函数表达"这个监听器
* 5. 与已退役 net_can_accept 的语义差异是**有意的**:本函数表达"这个监听器
* 还在服役",而非"这一轮一定有连接"
*/
int net_listener_armed(int sockfd) {
@@ -1479,6 +1453,15 @@ int net_listener_armed(int sockfd) {
if (p_sock == NULL) {
return 0;
}
/* 连接已到、应用尚未 accept:单连接模型下 CONNECT 处理(net_poll 分派 /
* 探针补 accept / 对账② standalone accept)把监听 fd 转 ESTABLISHED,
* 这就是"待 accept"电平——**必须放行**(原 net_can_accept 唯一放行态;
* N34 阶段1 i104 台架实证:200ms 任务周期抢不过对账②的 ~400ms 去抖,
* 对账②先转 ESTABLISHED → 不放行则应用永不 accept、会话整场饿死)。
* 无 RPC 风暴风险:此态下 accept 必返 fd,一次调用即离开本分支 */
if (p_sock->state == NET_SOCK_STATE_ESTABLISHED) {
return 1;
}
/* 正常监听态:放行。net_lost_connect_probe(丢 CONNECT 补 accept)与常规
* accept 都发生在 net_accept_locked 内部,门控挡在外面就永远到不了 */
if (p_sock->state == NET_SOCK_STATE_LISTENING) {
@@ -3032,110 +3015,11 @@ int net_sock_state(int sockfd) {
return (int)s_net_socks[sockfd].state;
}
/*
* 函数功能:断连重同步(仅 MCU 侧状态,见头文件注释的适用场景)
* 入口参数:sockfd - Socket 描述符 int
* 出口参数:无
* 返回值:0 - 已同步/原本即一致;-1 - 失败
* 限定条件:连接已死且芯片已静默回收后调用(应用层判定)
* 函数说明:1. 适用:芯片已静默回收连接(无 DISCONNECT 中断)而本层状态滞留 ESTABLISHED
* 2. 状态收回 LISTENING 并重建监听,后续 accept 恢复正常
*/
int net_resync_listen(int sockfd) {
/* 消息体(按值拷入队列,netTask 改的是其副本) */
net_msg_t msg;
/* 描述符越界 → BADF */
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS) {
return NET_ERR_RETURN_1(NET_ERR_BADF);
}
/* 消息队列未创建(网络层未初始化)→ 通用错误 */
if (netMsgQueueHandle == NULL) {
return NET_ERR_RETURN_1(NET_ERR);
}
/* 消息类型:断连重同步(MCU 侧收回 LISTENING 并重建监听) */
msg.type = NET_MSG_RESYNC_LISTEN;
/* 目标 Socket */
msg.sockfd = sockfd;
/* 无超时(重同步立即完成) */
msg.timeout_ms = 0U;
/* 调用方任务句柄(netTask 凭它回传回执) */
msg.caller = xTaskGetCurrentTaskHandle();
/* 结果占位(netTask 侧覆写实际结果) */
msg.result = -1;
/* 入队:队列满时阻塞调用方直至有空位 */
if (osMessageQueuePut(netMsgQueueHandle, &msg, 0, osWaitForever) != osOK) {
/* 入队失败 → BUSY */
return NET_ERR_RETURN_1(NET_ERR_BUSY);
}
/* 阻塞等待 netTask 回执(两段式拾取,防丢失通知) */
msg.result = net_wait_result("rsl", sockfd);
/* 失败:恢复 errno(对外统一 -1) */
if (msg.result < 0) {
return NET_ERR_RETURN_1(msg.result);
}
/* 重同步成功(0) */
return 0;
}
/*
* 函数功能:无条件重新监听(芯片侧 open+listen,含 Trap 09 失败重试)
* 入口参数:sockfd - Socket 描述符 int
* 出口参数:无
* 返回值:0 - 成功;-1 - 失败
* 限定条件:TCP 监听型 Socket;net_init() 已完成
* 函数说明:1. 用于"断连后监听器可能已失位"的自愈场景(2026-09-22 run9 实证)
* 2. 一次性 net_listen 落在 Trap 09 窗口失败且被静默忽略 → 监听器永久丢失
* 3. 后续连接全部拒绝
* 4. 与 auto_relisten 内部路径同一实现(net_relisten_locked:单次尝试 + 非阻塞重试,见 net_listener_retry_tick)
* 5. 芯片侧已在监听时:首次 open 失败 → close 重建 → 监听短暂中断 ~1s
*/
int net_relisten(int sockfd) {
/* 消息体(按值拷入队列,netTask 改的是其副本) */
net_msg_t msg;
/* 描述符越界 → BADF */
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS) {
return NET_ERR_RETURN_1(NET_ERR_BADF);
}
/* 消息队列未创建(网络层未初始化)→ 通用错误 */
if (netMsgQueueHandle == NULL) {
return NET_ERR_RETURN_1(NET_ERR);
}
/* 消息类型:无条件重新监听(含 Trap 09 非阻塞重试) */
msg.type = NET_MSG_RELISTEN;
/* 目标 Socket */
msg.sockfd = sockfd;
/* 无超时(重监听立即尝试) */
msg.timeout_ms = 0U;
/* 调用方任务句柄(netTask 凭它回传回执) */
msg.caller = xTaskGetCurrentTaskHandle();
/* 结果占位(netTask 侧覆写实际结果) */
msg.result = -1;
/* 入队:队列满时阻塞调用方直至有空位 */
if (osMessageQueuePut(netMsgQueueHandle, &msg, 0, osWaitForever) != osOK) {
/* 入队失败 → BUSY */
return NET_ERR_RETURN_1(NET_ERR_BUSY);
}
/* 阻塞等待 netTask 回执(两段式拾取,防丢失通知) */
msg.result = net_wait_result("rel", sockfd);
/* 失败:恢复 errno(对外统一 -1) */
if (msg.result < 0) {
return NET_ERR_RETURN_1(msg.result);
}
/* 重监听成功(0) */
return 0;
}
/* N34 阶段1(F-NET-3):手动自愈 API net_resync_listen / net_relisten 已删除——
* RESYNC 语义并入对账⑤(net_reconcile_tick 自动认领"芯片静默回收回 LISTEN"形态,
* 200ms×2 去抖 + 1s 冷却沿用);RELISTEN 与 pump/D10 完全重叠且对健康监听器是
* footgun(无条件 open→USE→close 自毁 ~1s,原注释自认)。生产调用者本就为零
* (仅 test/board Phase 13,已改由自动机制接管)。历史实现见本文件 git 历史。 */
/*
+24 -46
View File
@@ -37,8 +37,9 @@ typedef enum {
NET_MSG_CONNECT,
NET_MSG_LISTEN,
NET_MSG_ACCEPT,
NET_MSG_RESYNC_LISTEN, /* 仅修 MCU 侧状态(ESTABLISHED→LISTENING),不发芯片命令 */
NET_MSG_RELISTEN, /* 无条件芯片侧重监听(open+listen,含 Trap 09 重试) */
/* N34 阶段1(F-NET-2/3):NET_MSG_RESYNC_LISTEN / NET_MSG_RELISTEN 已删——
* 静默回收由对账⑤自动认领(net_reconcile_tick),重建由 pump/D10 单一
* 所有者负责;手动自愈 API 面退役(生产调用者本就为零) */
} net_msg_type_t;
typedef struct {
@@ -150,48 +151,38 @@ int net_listen(int sockfd);
*/
int net_accept(int sockfd, struct net_sockaddr *addr, int *addrlen);
/*
* 函数功能:查询监听 Socket 当前是否有可接受的连接(**纯 RAM 读,不发起 RPC**)
* 入口参数:sockfd - 监听 Socket 描述符 int
* 出口参数:无
* 返回值:1 - 有连接可接受;0 - 无(含描述符无效) int
* 限定条件:net_init() 已调用;任意任务上下文(含 uiTask 等实时性敏感任务)
* 函数说明:1. 只读 Socket 控制块状态,**不经消息队列、不等 netTask 回执** →
* 调用方不会被 netTask 的忙碌程度拖住(对比 net_accept:
* osMessageQueuePut(osWaitForever) + xTaskNotifyWait(portMAX_DELAY),
* 两者均**无上界**)
* 2. 判据与 net_accept_locked() 完全一致:单连接模型下监听 Socket 自身
* 在 CONNECT 事件后转 ESTABLISHED,即"有连接可接受"
* 3. 用途 = **门控**:无连接时直接跳过 net_accept(),把"每轮一次无谓
* RPC"降为 0(UI 推流 10 ms 轮询 = 100 msg/s 队列压力的根因)
* 4. 跨任务读单字:netTask 是唯一写者,最坏两种结果都无害——
* 晚一轮发现(netTask 每 1 ms 就 net_poll 一次),或扑空
* (net_accept 返回 -1 + NET_ERR_WOULDBLOCK)
* 5. 丢 CONNECT 中断由 net_reconcile_tick()(200 ms 对账 + 去抖)自愈,
* 本函数**不承担**自愈责任
*/
int net_can_accept(int sockfd);
/* N34 阶段1(F-NET-4):net_can_accept 已退役——只认 ESTABLISHED 的旧门控会把
* "监听器在服役、本轮待 CONNECT"窗口全挡掉(丢 CONNECT 探针因此从不运行);
* 监听 accept 门控统一用 net_listener_armed(),recv 门控 net_can_read() 保留。 */
/*
* 函数功能:查询监听 Socket 是否处于"应该尝试 accept"的就绪态(**纯 RAM 读,不发起 RPC**)
* 入口参数:sockfd - 监听 Socket 描述符 int
* 出口参数:无
* 返回值:1 - 就绪(LISTENING,或 CLOSED + auto_relisten = 断连待重建态);0 - 不就绪 int
* 返回值:1 - 就绪(ESTABLISHED 待 accept / LISTENING / CLOSED+auto_relisten
* +残留数据);0 - 不就绪 int
* 限定条件:net_init() 已调用;任意任务上下文
* 函数说明:1. N-32(P1 不变量 II):取代 `net_can_accept()`(只认 ESTABLISHED)作为
* ui_net_sink 的 accept 门控——原门控使 :7003 永不进 `net_accept()`,
* 函数说明:1. N-32(P1 不变量 II,N34 阶段1 补齐 ESTABLISHED 放行):取代已退役的
* `net_can_accept()`(只认 ESTABLISHED)作为监听 accept 门控——原门控使
* :7003 永不进 `net_accept()`,
* `net_lost_connect_probe`(丢 CONNECT 中断补 accept,N-30 实证的
* "数据被吞/425"主根因修复)对该端口等于从未运行
* 2. 放行 LISTENING:探针与 "closed-but-readable 接受" 都发生在
* `net_accept_locked` **内部**,门控挡在外面就永远到不了
* 2b. 放行 ESTABLISHED(N34 阶段1 台架修正):单连接模型下 CONNECT 处理
* (net_poll 分派 / 探针补 accept / 对账② standalone accept)把监听 fd
* 转 ESTABLISHED,这正是"待 accept"电平——漏放行则任务周期慢的消费者
* (i104 200ms 抢不过对账② ~400ms 去抖)永不 accept、会话整场饿死
* (2026-10-13 实测全 18 项 FAIL)。此态 accept 必返 fd,无风暴风险
* 3. 放行 CLOSED+auto_relisten **且 readable≠0**(有残留数据):
* 保住 N-30 的 "closed-but-readable" 接受;**无残留时必须返回 0**,
* 否则调用方会在整个 CLOSED 窗口内每轮发起 `net_accept`(RPC 无上界)
* → 队列积压 → 喂不上狗(2026-10-12 P2a 台架实测 48 条 task stall)。
* 重建由 `net_listener_pump` 负责,调用方无需催促
* 4. 与 `net_can_accept` 的语义差异**有意为之**:本函数回答"这个监听器
* 这一轮**值不值得**发起一次 accept",而非"一定有连接"
* 5. 跨任务读单字,同 `net_can_accept` 的最坏情形论证
* 4. 与已退役 `net_can_accept` 的关系:本函数 = 其 ESTABLISHED 放行态
* ∪ LISTENING ∪ CLOSED+残留;回答"这个监听器这一轮**值不值得**发起
* 一次 accept",而非"一定有连接"
* 5. 跨任务读单字,同 `net_can_read` 的最坏情形论证
*/
int net_listener_armed(int sockfd);
@@ -346,23 +337,10 @@ int net_get_errno(void);
*/
int net_sock_state(int sockfd);
/*
* 函数功能:断连重同步——仅把 MCU 侧状态对齐到 LISTENING,不发任何芯片命令
* 适用场景:芯片"静默回收"(peer 发 FIN 后芯片自行把 Socket 回收回监听
* 且不发 DISCONNECT 中断,2026-09-22 台架实证)→ MCU 状态滞留
* ESTABLISHED。调用方须确信连接已死/芯片已回收(如会话超时中止后);
* 连接仍存活时严禁调用(会把活连接标记成监听态)。
* 返回值:0 - 已同步(或原本即 LISTENING);-1 - 失败(描述符/队列无效)
*/
int net_resync_listen(int sockfd);
/*
* 函数功能:无条件重新监听(芯片侧 open+listen,含 Trap 09 失败重试)
* 适用场景:断连后需确保监听器确定在位的自愈(监听短暂中断 ~1s,调用方
* 的重连逻辑须容忍)
* 返回值:0 - 成功;-1 - 失败
*/
int net_relisten(int sockfd);
/* N34 阶段1(F-NET-3):net_resync_listen / net_relisten 声明已删——
* 静默回收形态由对账⑤自动认领(≤400ms 去抖 + 1s 冷却,纪律"不发芯片命令"
* 原样保留在认领动作内);监听重建由 pump(100ms 周期)/D10(30ms 重试)
* 单一所有者。勿再引入"应用层手动自愈"入口。 */
/*
* 函数功能:将 IP 地址字符串转为网络字节序