refactor(net): N-32 §10 监听生命周期抽为 net_listener.c(纯搬移)
把监听生命周期从 net_socket.c 整体抽出为真机专属模块 net_listener.c: 7 个函数 + 7 个状态数组 + 8 个常量 ≈470 行。 net_socket.c 4126 → 3656 行(-471)。 模块边界(详见 net_listener.h 头注释): - 搬入:relisten_locked / try / clear / done / retry_tick / pump / drain_chip_rx - 留在 net_socket.c:net_listen_locked(应用 API 路径)/ net_accept_locked (产出连接)/ net_lost_connect_probe(补 accept)/ net_listener_armed (双端公开 API)/ net_resync_listen / net_reconcile_tick / net_sock_t 字段 接口从「8 处散点直调 + 2 处直接写状态数组」收敛为 4 个窄接口: net_listener_settle(fd, level) / _try(fd) / _clear(fd) / _retry_tick() 新模块**不访问 s_net_socks**(改用 net_get_sock + ch395_sock)。 搬移中必要的两处可见性提升(非纯机械搬移,已注释留痕): - net_set_errno 由 static 提升为模块间可见(NET_ERR_RETURN_1 宏随迁头文件) - 新增 net_pending_active(fd),替代直接读 s_net_pending[i].active (L3 保险语义原样保留) 真机专属:net_listener.c 依赖 ch395f_* SPI 驱动,sim(Winsock)无对应 → 只注册 Keil 端,并在 check_registration.py 的 SIM_EXEMPT **显式点名** (不整目录一刀切,遵脚本既定原则)。 验证(纯搬移的可证判据): - 尺寸:**RW-data 980 与 ZI-data 143060 完全不变**(数据总量不变); Code 197236→197228(-8)、RO-data 17012→17000(-12)——接口改造的 极小开销,相对 471 行搬移可忽略 - 门禁全绿(cppcheck / C 规范 / 双端注册 / 主机侧单测 12 套 / mem-numbers) - @test PASS + 回读 No difference found - 全量回归::21 0ms×50 与 100ms×50 各 50/50;:8888 20/20;:7003 10/10; ftp_regression 11/11;Trap23 abort/graceful 各 5/5 - 设备侧:orphan listener **0**(保持 P2a 基线)、pump 留痕正常
This commit is contained in:
@@ -0,0 +1,554 @@
|
||||
/*
|
||||
* 模块名称:Network Listener Lifecycle
|
||||
* 模块功能:监听器生命周期(真机专属)—— 维持 fd 处于 LISTEN 态的全部机制:
|
||||
* 重建(open+listen)、重建前的沉降/隔离(Trap 23)、排空 RX 残渣、
|
||||
* D10 非阻塞重试、周期性自愈 pump(唯一的周期性重建决策者)。
|
||||
* 适用平台:STM32F407ZGTx + CH395F 以太网芯片
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-10-12
|
||||
* 修改记录:
|
||||
* 2026-10-12 王建锋 从 net_socket.c 整体抽出监听生命周期(N-32 §10 模块化)
|
||||
*
|
||||
* 职责与边界见 net_listener.h 头注释;设计依据见
|
||||
* docs/子系统设计/网络Socket监听生命周期设计说明.md §10。
|
||||
*
|
||||
* 模块内分层:
|
||||
* ① 策略常量与私有状态(s_relisten_*)
|
||||
* ② 机制:net_drain_chip_rx(排空)/ net_relisten_locked(重建一次)
|
||||
* ③ 对外接口:settle / try / clear / retry_tick / pump
|
||||
*
|
||||
* 依赖(全部为单向,无反向 include):
|
||||
* net_types.h(net_sock_t)/ net_socket.h(net_get_sock / NET_MAX_SOCKETS /
|
||||
* net_set_errno / NET_ERR_* / net_pending_active)/ ch395f.h(驱动)/
|
||||
* dbg_log.h / main.h(HAL_GetTick)
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include "cmsis_os.h"
|
||||
|
||||
#include "net_socket.h" /* net_sock_t / net_get_sock / NET_MAX_SOCKETS / net_set_errno */
|
||||
#include "net_listener.h"
|
||||
#include "ch395f.h"
|
||||
#include "test_config.h" /* TEST_LATENCY_PROBE(链路时延探针编译开关) */
|
||||
#include "net_latency_test.h" /* L3 探针接口(relisten 恢复计时采样) */
|
||||
#include "main.h"
|
||||
|
||||
#include "dbg_log.h"
|
||||
|
||||
/* ===== ① 策略常量与私有状态 ===== */
|
||||
|
||||
/* Trap 23(2026-09-23 生产路径实测):异常释放(芯片 TIMEOUT,例如客户端中止上传)
|
||||
* 后的**隔离期**。实测时序:中止时对端仍有在途数据 → 芯片 TIMEOUT(此时 MCU 侧 recv=0)
|
||||
* 在途段随后落进芯片 RX 缓冲(约 2KB 残渣)
|
||||
* 若立即 open+listen,残渣被下一条连接当作自己的数据读走
|
||||
* 新上传只收 2KB 就被判定结束、回报成功(静默截断)
|
||||
* 处置:异常释放的 socket 先隔离 NET_RELISTEN_QUARANTINE_MS:期间持续排空 RX、主动 close 并排空中断
|
||||
* 等芯片回到 CLOSED/TCP_CLOSED;隔离期满才 open+listen
|
||||
* 上限 NET_RELISTEN_SETTLE_MS 后强制放行,保证监听器不会永久失效
|
||||
* 正常 FIN 释放(handle_disconnect_event)不走此路径——既有行为零改动
|
||||
*/
|
||||
#define NET_RELISTEN_QUARANTINE_MS 400U /* 异常释放最短隔离(等在途数据落尽 + 排空残渣) */
|
||||
#define NET_RELISTEN_GRACEFUL_MS 100U /* 正常释放最短沉降(等芯片完成上一条连接的收尾) */
|
||||
#define NET_RELISTEN_SETTLE_MS 800U /* 隔离上限(须 < NET_RELISTEN_DEADLINE_MS,
|
||||
* 否则隔离耗尽重试预算 → 监听被放弃,2026-09-23 实测) */
|
||||
/* 重监听前"清芯片收尾状态"的两个等级(s_relisten_quarantine 取值,取值语义见 net_listener.h)
|
||||
* 2026-09-24 台架实证:正常释放走的是既有"不复位芯片收尾状态"的路径
|
||||
* 残留的 DISCONNECT 会在复用同一 fd 的下一条连接上被投递(accept 后约 10~20ms)
|
||||
* 下一条上传被静默截断到 2048B(一个芯片 RX 缓冲)且回报 226
|
||||
* 只清理不等待(0ms)实测无效(4/4 仍截断):芯片需要时间完成上一条连接的内部收尾
|
||||
* 给足沉降即 4/4 通过
|
||||
*/
|
||||
#define NET_RELISTEN_SETTLE_QUARANTINE 1U
|
||||
#define NET_RELISTEN_SETTLE_GRACEFUL 2U
|
||||
|
||||
static uint8_t s_relisten_quarantine[NET_MAX_SOCKETS]; /* 0=免清;1=异常释放;2=正常释放 */
|
||||
static uint32_t s_relisten_settle_start[NET_MAX_SOCKETS]; /* 隔离起点(0 = 未开始) */
|
||||
static uint32_t s_relisten_settle_dead[NET_MAX_SOCKETS]; /* 隔离上限到期时刻 */
|
||||
|
||||
/* ===== Trap 09 非阻塞重监听重试(D10 修复,2026-09-22 M3 实测) =====
|
||||
* 旧实现:open 失败 → close → osDelay(500) → 重试,netTask 忙等 500ms(全网冻结)
|
||||
* 且实测恢复 588ms、期间客户端重连被拒
|
||||
* 新实现:首次失败即登记待重试,由 netTask 每轮 net_process_messages() 到点重试
|
||||
* 恢复时间 ≈ NET_RELISTEN_RETRY_MS 量级,任何时刻都不阻塞任务
|
||||
*/
|
||||
#define NET_RELISTEN_RETRY_MS 30U /* 重试间隔(Trap 09 沉降窗口;实测 100ms 首次必成,
|
||||
* 收紧到 30ms 以缩短恢复时间——未成则下一轮到点再来) */
|
||||
#define NET_RELISTEN_DEADLINE_MS 2000U /* 放弃时限(自首次尝试起) */
|
||||
|
||||
static uint8_t s_relisten_wait[NET_MAX_SOCKETS]; /* 1 = 有待重试的重监听 */
|
||||
static uint32_t s_relisten_next[NET_MAX_SOCKETS]; /* 下次重试时刻(tick) */
|
||||
static uint32_t s_relisten_dead[NET_MAX_SOCKETS]; /* 放弃时刻(tick) */
|
||||
#if defined(TEST_LATENCY_PROBE)
|
||||
static lat_tmark_t s_relisten_mark[NET_MAX_SOCKETS]; /* 恢复计时起点(首次尝试) */
|
||||
#endif
|
||||
|
||||
/* ===== ② 机制 ===== */
|
||||
|
||||
/*
|
||||
* 函数功能:清空芯片该 socket 的 RX 残留(Trap 23)
|
||||
* 入口参数:sockfd - Socket 描述符 (0~7) int
|
||||
* 出口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:netTask 上下文;socket 即将 close/open(数据已无意义,丢弃)
|
||||
* 函数说明:1. 中止/异常断开时芯片 RX 缓冲可能残留上一条连接的数据(实测残留约一个缓冲 = 2048B)
|
||||
* 2. 残渣带入下一条连接会使其在约 2KB 处被芯片拆链(上传被静默截断)
|
||||
* 3. 关闭前主动读空即清除;分块读避免新增静态缓冲
|
||||
* (2026-10-09 实测:ARMCC 把本 scratch 与同函数内生命周期不重叠的
|
||||
* 192B 日志缓冲叠放,局部缓冲边际栈成本仅 ~16B,静态化反亏 256B BSS,勿再"优化")
|
||||
*/
|
||||
static void net_drain_chip_rx(int sockfd) {
|
||||
/* 复用分块缓冲(256B/块,避免新增静态缓冲) */
|
||||
uint8_t scratch[256];
|
||||
|
||||
/* 上限 16×256 = 4KB(缓冲仅 2KB),防死循环 */
|
||||
for (int guard = 0; guard < 16; guard++) {
|
||||
/* 查询芯片 RX 可用字节数 */
|
||||
int n = (int)ch395f_get_recv_len((uint8_t)sockfd);
|
||||
/* 已排空 → 结束 */
|
||||
if (n <= 0) {
|
||||
break;
|
||||
}
|
||||
/* 单块以缓冲容量为上限 */
|
||||
if (n > (int)sizeof(scratch)) {
|
||||
n = (int)sizeof(scratch);
|
||||
}
|
||||
/* 丢弃该块(读即清) */
|
||||
ch395f_read_recv_buf((uint8_t)sockfd, scratch, (uint16_t)n);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:单连接模式断连后自动重新进入 LISTEN(不清空控制块,保留 fd)
|
||||
* 入口参数:sockfd - 已断开(CLOSED)的 TCP 监听型 Socket 描述符 (0~7) int
|
||||
* 出口参数:无
|
||||
* 返回值:0 - 成功,-1 - 失败
|
||||
* 限定条件:netTask 上下文;控制块 in_use == 1;保留 local_port/auto_relisten
|
||||
* 函数说明:1. 复用原本地端口与协议,重新 OPEN + TCP_LISTEN
|
||||
* 2. 仅将 state 置回 LISTENING,不 memset(fd 与 auto_relisten 保持不变)
|
||||
* 3. 实现 BSD 风格"监听 Socket 常驻",应用层无需手动重监听
|
||||
* 4. **单次尝试**:失败立即返回(不 osDelay 忙等)
|
||||
* 5. Trap 09 沉降交给 net_listener_try / net_listener_retry_tick 非阻塞重试(见下)
|
||||
* 6. 旧实现在此 osDelay(500) 忙等,netTask 冻结 500ms、实测恢复 588ms(M3 台架 D10)
|
||||
* 7. N-32 §10:fd 由参数传入(不再用 `p_sock - s_net_socks` 反推),
|
||||
* 控制块经 net_get_sock() 取 —— 本模块**不访问** net_socket.c 的 static 数组
|
||||
*/
|
||||
static int net_relisten_locked(int sockfd) {
|
||||
/* 芯片命令返回状态 */
|
||||
uint8_t status;
|
||||
/* Socket 控制块(fd 由调用方保证有效) */
|
||||
net_sock_t *p_sock = net_get_sock(sockfd);
|
||||
|
||||
/* 控制块无效:拒绝(不应发生,调用方已判过) */
|
||||
if (p_sock == NULL) {
|
||||
return NET_ERR_RETURN_1(NET_ERR_BADF);
|
||||
}
|
||||
|
||||
/* 释放隔离期(Trap 23):重监听前先清芯片收尾状态 */
|
||||
if (s_relisten_quarantine[sockfd] != 0U) {
|
||||
/* 芯片状态两字节:[0]=Socket,[1]=TCP */
|
||||
uint8_t st[2] = {0, 0};
|
||||
/* 当前 tick */
|
||||
uint32_t now = HAL_GetTick();
|
||||
/* 隔离等级(1=异常释放 2=正常释放) */
|
||||
uint8_t lvl = s_relisten_quarantine[sockfd];
|
||||
/* 最短等待由释放来源决定(NET_RELISTEN_SETTLE_*):异常释放 400ms(等在途数据落尽)
|
||||
* 正常释放 100ms(等芯片内部收尾) */
|
||||
uint32_t hold = (lvl == NET_RELISTEN_SETTLE_QUARANTINE) ? NET_RELISTEN_QUARANTINE_MS
|
||||
: NET_RELISTEN_GRACEFUL_MS;
|
||||
/* 日志标签(台架分辨触发路径) */
|
||||
const char *tag = (lvl == NET_RELISTEN_SETTLE_QUARANTINE) ? "quarantine" : "settle";
|
||||
|
||||
/* 首次进入隔离期:记录起点与上限 */
|
||||
if (s_relisten_settle_start[sockfd] == 0U) {
|
||||
/* 隔离起点 = 本 tick */
|
||||
s_relisten_settle_start[sockfd] = now;
|
||||
/* 隔离上限 = 起点 + 800ms */
|
||||
s_relisten_settle_dead[sockfd] = now + NET_RELISTEN_SETTLE_MS;
|
||||
}
|
||||
/* 隔离期内持续排空 RX 残渣 */
|
||||
net_drain_chip_rx(sockfd);
|
||||
/* 主动关:清旧连接收尾状态 */
|
||||
ch395f_close_socket((uint8_t)sockfd);
|
||||
/* 读即清:本 Socket 中断状态寄存器 */
|
||||
(void)ch395f_get_sock_int_status((uint8_t)sockfd);
|
||||
/* 读即清:全局中断状态寄存器 */
|
||||
(void)ch395f_get_glob_int_status_all();
|
||||
/* 读芯片当前状态(两字节) */
|
||||
ch395f_get_socket_status((uint8_t)sockfd, st);
|
||||
|
||||
/* 芯片尚未回到 CLOSED/TCP_CLOSED,或未满最短等待 */
|
||||
if (((st[0] != CH395F_SOCKET_CLOSED) || (st[1] != CH395F_TCP_CLOSED) ||
|
||||
((uint32_t)(now - s_relisten_settle_start[sockfd]) < hold))) {
|
||||
/* 未到上限:交重试轮询(30ms 后复检) */
|
||||
if ((int32_t)(now - s_relisten_settle_dead[sockfd]) < 0) {
|
||||
DBG_INFO("sock%d relisten %s (sock=0x%02X tcp=0x%02X)",
|
||||
sockfd, tag, st[0], st[1]);
|
||||
/* 置 BUSY(结果回传调用方) */
|
||||
(void)net_set_errno(NET_ERR_BUSY);
|
||||
/* 交重试轮询(30ms 后复检) */
|
||||
return -1;
|
||||
}
|
||||
/* 隔离上限已至:强制放行(防监听器永久失效) */
|
||||
DBG_INFO("sock%d relisten %s timeout, force open (sock=0x%02X tcp=0x%02X)",
|
||||
sockfd, tag, st[0], st[1]);
|
||||
}
|
||||
/* 隔离完成(或强制放行) */
|
||||
s_relisten_quarantine[sockfd] = 0U;
|
||||
/* 清隔离起点(回到"未开始") */
|
||||
s_relisten_settle_start[sockfd] = 0U;
|
||||
/* 清上限时刻 */
|
||||
s_relisten_settle_dead[sockfd] = 0U;
|
||||
}
|
||||
|
||||
/* 重置协议类型为 TCP */
|
||||
ch395f_set_proto_type((uint8_t)sockfd, CH395F_PROTO_TYPE_TCP);
|
||||
/* 本地端口(沿用原监听) */
|
||||
ch395f_set_sour_port((uint8_t)sockfd, p_sock->local_port);
|
||||
|
||||
/* 打开芯片侧 Socket */
|
||||
status = ch395f_open_socket((uint8_t)sockfd);
|
||||
/* open 失败 → BUSY(Trap 09:芯片需沉降时间才能重开) */
|
||||
if (status != CH395F_ERR_SUCCESS) {
|
||||
/* Trap 09:断开后芯片需沉降时间才能重新 open。此处只清理并返回
|
||||
* (严禁 osDelay 忙等)→ 由重试轮询到点再来。 */
|
||||
/* 先关闭,交重试轮询(30ms 后复检) */
|
||||
ch395f_close_socket((uint8_t)sockfd);
|
||||
/* 置 BUSY(结果回传调用方) */
|
||||
(void)net_set_errno(NET_ERR_BUSY);
|
||||
/* 交重试轮询(30ms 后复检) */
|
||||
return -1;
|
||||
}
|
||||
/* 下发 TCP LISTEN 命令 */
|
||||
status = ch395f_tcp_listen((uint8_t)sockfd);
|
||||
/* LISTEN 失败 → BUSY(同样交重试) */
|
||||
if (status != CH395F_ERR_SUCCESS) {
|
||||
/* 置 BUSY(结果回传调用方) */
|
||||
return NET_ERR_RETURN_1(NET_ERR_BUSY);
|
||||
}
|
||||
|
||||
/* 状态置回 LISTENING(fd 保留) */
|
||||
p_sock->state = NET_SOCK_STATE_LISTENING;
|
||||
/* N-32(P1,设计文档 §5 P1.1 的关键约定):此处**刻意不清 session_dead**。
|
||||
* 重建监听只是"芯片侧恢复就位",与"应用是否已确认上一条会话结束"是两件事;
|
||||
* 清了它,抢先 relisten 之后消费者就再也得不到 EOF(B10"消费者卡死"原型)。
|
||||
* session_dead 只由新 CONNECT 成功(handle_connect_event)或 memset
|
||||
* (net_close/net_socket)清零。 */
|
||||
/* 新监听不得继承上一条连接的释放原因(Trap 23 待办 #3) */
|
||||
p_sock->released_abnormal = 0U;
|
||||
/* N-30(Trap 25):清零前把对端端口转登为幽灵甄别基准 */
|
||||
p_sock->last_peer_port = p_sock->remote_port;
|
||||
/* N-30:新监听会话,丢中断去抖计数清零 */
|
||||
p_sock->lost_conn_seen = 0U;
|
||||
/* 清对端端口(新连接待用) */
|
||||
p_sock->remote_port = 0;
|
||||
/* 清对端 IP */
|
||||
p_sock->remote_ip = 0;
|
||||
/* 清对端 IP 数组 */
|
||||
memset(p_sock->remote_ip_arr, 0, sizeof(p_sock->remote_ip_arr));
|
||||
/* 新开 Socket 发送缓冲按占用计(由 SEND_OK 清除) */
|
||||
p_sock->send_ready = 0;
|
||||
/* 清接收字节统计(新连接) */
|
||||
p_sock->recv_bytes = 0;
|
||||
/* auto_relisten 保持为 1,下次断连仍自动重监听 */
|
||||
|
||||
/* 读即清:仅本 Socket 的中断状态(防上一条会话残留误触发)。
|
||||
* 原为"全 8 个 Socket + 全局"读清,与 net_listen_locked 同源(B3,机制
|
||||
* 与 git 考古见该处注释):本处是每次断连 auto-relisten 的热路径,全量清
|
||||
* 会持续吞掉其它在用 Socket 的 CONNECT/DISCONNECT 等边沿事件。全局中断
|
||||
* 寄存器交 net_poll 集中读清(AGENTS 模块约定)。 */
|
||||
(void)ch395f_get_sock_int_status((uint8_t)sockfd);
|
||||
|
||||
/* 留痕:监听器重建完成 */
|
||||
DBG_INFO("sock%d auto-relisten ok", sockfd);
|
||||
/* 重监听成功 */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ===== ③ 对外接口 ===== */
|
||||
|
||||
/*
|
||||
* 函数功能:登记「本 fd 的会话已结束,重建监听前需沉降/隔离」(契约见 net_listener.h)
|
||||
* 入口参数:sockfd - Socket 描述符 (0~7) int
|
||||
* level - 沉降等级(net_listener_settle_t,由释放来源决定)
|
||||
* 出口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:netTask 上下文;由 handle_disconnect_event / handle_timeout_event 调用
|
||||
* 函数说明:1. 只置事实,不做动作 —— 重建由 net_listener_pump / net_listener_try 负责
|
||||
* 2. 越界 sockfd 静默忽略(与旧直接写 s_relisten_quarantine 行为一致)
|
||||
*/
|
||||
void net_listener_settle(int sockfd, net_listener_settle_t level) {
|
||||
/* 描述符越界:静默忽略(与旧直接写数组行为一致,不设错误码) */
|
||||
if ((sockfd < 0) || (sockfd >= NET_MAX_SOCKETS)) {
|
||||
return;
|
||||
}
|
||||
/* 登记沉降/隔离等级 —— 只置事实,重建由 pump / try 负责 */
|
||||
s_relisten_quarantine[sockfd] = (uint8_t)level;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:清除本 fd 的待重监听重试状态(关闭/新建 fd 时调用)
|
||||
* 入口参数:sockfd - Socket 描述符 (0~7)
|
||||
* 出口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:无
|
||||
* 函数说明:1. 清"待重试"标志与重试时刻,fd 关闭/新建时调用(防残留重试)
|
||||
*/
|
||||
void net_listener_clear(int sockfd) {
|
||||
/* 描述符越界:直接返回(本函数不设错误码) */
|
||||
if ((sockfd < 0) || (sockfd >= NET_MAX_SOCKETS)) {
|
||||
return;
|
||||
}
|
||||
/* 清"待重试"标志 */
|
||||
s_relisten_wait[sockfd] = 0U;
|
||||
/* 清下次重试时刻 */
|
||||
s_relisten_next[sockfd] = 0U;
|
||||
/* 清放弃时刻 */
|
||||
s_relisten_dead[sockfd] = 0U;
|
||||
/* Trap 23:一并清沉降/隔离状态 */
|
||||
s_relisten_settle_dead[sockfd] = 0U;
|
||||
/* 清沉降起点 */
|
||||
s_relisten_settle_start[sockfd] = 0U;
|
||||
/* 一并清隔离标志:关闭/重建即确定性释放,
|
||||
* 防同 fd 下一生成继承上轮的 400/800ms
|
||||
* 隔离等待(2026-09-24 台架发现跨轮残留) */
|
||||
s_relisten_quarantine[sockfd] = 0U;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:标记重监听完成(清重试状态;探针构建下报告恢复计时样本)
|
||||
* 入口参数:sockfd - Socket 描述符 (0~7)
|
||||
* 出口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:netTask 上下文
|
||||
* 函数说明:1. 重监听成功收尾:清重试状态;探针构建(TEST_LATENCY_PROBE)下上报恢复计时样本
|
||||
*/
|
||||
static void net_relisten_done(int sockfd) {
|
||||
/* 清重试状态(复用清除函数) */
|
||||
net_listener_clear(sockfd);
|
||||
#if defined(TEST_LATENCY_PROBE)
|
||||
/* 上报恢复计时样本(首次尝试 → 成功) */
|
||||
lat_probe_add_seg("relisten", lat_mark_us(&s_relisten_mark[sockfd]));
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:尝试重建监听一次(**不阻塞**);失败则登记"待重试"(契约见 net_listener.h)
|
||||
* 入口参数:sockfd - Socket 描述符 (0~7) int
|
||||
* 出口参数:无
|
||||
* 返回值:0 - 成功(监听器已就绪);-1 - 本次失败或已在重试队列中 int
|
||||
* 限定条件:netTask 上下文;fd 为监听型(auto_relisten)
|
||||
* 函数说明:1. 已在重试队列时直接返回 -1(不重复尝试、不重置重试时刻)——
|
||||
* 否则每 10ms 一次的 accept 轮询会不断把重试时刻往后推,
|
||||
* 导致永不重试(重试饥饿)
|
||||
* 2. N-32 §10:替代此前散落在 net_socket.c 各处的 net_relisten_try()
|
||||
*/
|
||||
int net_listener_try(int sockfd) {
|
||||
/* 当前 tick */
|
||||
uint32_t now = HAL_GetTick();
|
||||
|
||||
/* 描述符越界 → BADF */
|
||||
if ((sockfd < 0) || (sockfd >= NET_MAX_SOCKETS)) {
|
||||
return NET_ERR_RETURN_1(NET_ERR_BADF);
|
||||
}
|
||||
/* 已在重试队列:不重复尝试、不重置时刻(防重试饥饿) */
|
||||
if (s_relisten_wait[sockfd] != 0U) {
|
||||
(void)net_set_errno(NET_ERR_BUSY);
|
||||
/* 已在重试队列:由 tick 负责 */
|
||||
return -1;
|
||||
}
|
||||
#if defined(TEST_LATENCY_PROBE)
|
||||
/* 恢复计时自首次尝试起 */
|
||||
lat_mark(&s_relisten_mark[sockfd]);
|
||||
#endif
|
||||
/* 本次尝试成功 */
|
||||
if (net_relisten_locked(sockfd) == 0) {
|
||||
/* 标记完成(清状态 + 探针样本) */
|
||||
net_relisten_done(sockfd);
|
||||
/* 监听器已就绪 */
|
||||
return 0;
|
||||
}
|
||||
/* 登记"待重试" */
|
||||
s_relisten_wait[sockfd] = 1U;
|
||||
/* 下次重试时刻 = now + 30ms */
|
||||
s_relisten_next[sockfd] = now + NET_RELISTEN_RETRY_MS;
|
||||
/* 放弃时刻 = now + 2s */
|
||||
s_relisten_dead[sockfd] = now + NET_RELISTEN_DEADLINE_MS;
|
||||
/* 本次失败(重试交 tick) */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:D10 重试轮询(netTask 每轮调用一次,非阻塞)
|
||||
* 入口参数:无
|
||||
* 出口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:netTask 上下文(由 net_process_messages 每轮调用)
|
||||
* 函数说明:1. 到点重试待办的重监听;成功即报告探针样本
|
||||
* 2. 本 fd 有在途挂起 recv 时跳过(net_pending_active,L3 保险:
|
||||
* 重开 Socket 会摧毁在途连接)
|
||||
* 3. 超过 NET_RELISTEN_DEADLINE_MS 放弃并记错误日志(防监听器永久失效)
|
||||
*/
|
||||
void net_listener_retry_tick(void) {
|
||||
/* 当前 tick */
|
||||
uint32_t now = HAL_GetTick();
|
||||
/* Socket 循环索引 */
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NET_MAX_SOCKETS; i++) {
|
||||
/* 本 fd 无待重试重监听:跳过 */
|
||||
if (s_relisten_wait[i] == 0U) {
|
||||
continue;
|
||||
}
|
||||
/* 本 fd 有在途挂起 recv:重开 Socket 会摧毁在途连接(L3 保险) */
|
||||
if (net_pending_active(i) != 0U) {
|
||||
continue;
|
||||
}
|
||||
/* 未到点(tick 差值按有符号比较,抗回绕) */
|
||||
if ((int32_t)(now - s_relisten_next[i]) < 0) {
|
||||
continue;
|
||||
}
|
||||
/* 已到放弃时限 */
|
||||
if ((int32_t)(now - s_relisten_dead[i]) >= 0) {
|
||||
/* 清重试状态 */
|
||||
net_listener_clear(i);
|
||||
/* 放弃并记错误日志 */
|
||||
DBG_ERROR("sock%d relisten give up (Trap 09 not cleared)", i);
|
||||
continue;
|
||||
}
|
||||
/* 重试成功 */
|
||||
if (net_relisten_locked(i) == 0) {
|
||||
/* 标记完成(清状态 + 探针样本) */
|
||||
net_relisten_done(i);
|
||||
/* 留痕:重试成功 */
|
||||
DBG_INFO("sock%d relisten retry ok", i);
|
||||
} else {
|
||||
/* 仍未成:置下次重试时刻 */
|
||||
s_relisten_next[i] = now + NET_RELISTEN_RETRY_MS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 监听器 pump 周期(对账 200ms 的 1/2:比它快、又不至于像 50ms 那样
|
||||
* 频繁 close/open 芯片 Socket 与消费者收发撞车——2026-10-12 台架实测
|
||||
* 50ms 会把 uiTask 最坏单轮从 172ms 推到 1034ms) */
|
||||
#define NET_LISTENER_PUMP_PERIOD_MS 100U
|
||||
/* pump 去抖与冷却(沿用原对账③④ 的实测结论,取值不变):
|
||||
* 2026-09-23 实证——无去抖时监听被反复重建直至放弃 */
|
||||
#define NET_LISTENER_STABLE_N 2U /* 连续失配次数(去抖,≈200ms) */
|
||||
#define NET_LISTENER_COOLDOWN_MS 1000U /* 同一 socket 两次动手的最小间隔(防抖成风暴) */
|
||||
|
||||
/*
|
||||
* 函数功能:监听器 pump(N-32 P2「单一所有者」)—— 本层**唯一**的周期性
|
||||
* 重建监听决策者
|
||||
* 入口参数:无
|
||||
* 出口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:netTask 上下文(由 net_process_messages 每轮调用)
|
||||
* 函数说明:1. 设计意图(设计文档 §4 不变量 II):其它所有位置只**置事实**
|
||||
* (session_dead / 状态迁移 / 事件派发),不**做动作**——于是
|
||||
* "谁先看到 DISCONNECT 就谁重建"这类竞争在结构上消失
|
||||
* 2. 覆盖三种情形(前两种原属对账③④,第三种为 U11 缺口):
|
||||
* ① 本层 LISTENING 而芯片监听已失位 → 重建(原对账③)
|
||||
* ② 本层 CLOSED(会话已结束)而芯片不在 LISTEN → 重建(原对账④)
|
||||
* ③ 本层 CLOSED 而芯片**正在** LISTEN → **只做状态重同步**
|
||||
* (U11):芯片监听器是健康的,重下发 open+listen 会因 USE
|
||||
* 错误走进关闭分支、把正听着的 Socket 关掉(U12 同源事故);
|
||||
* 只把本层状态收回 LISTENING,**不发任何芯片命令**
|
||||
* 3. 去抖/冷却沿用对账的实测结论(2026-09-23 实证:无去抖时监听被
|
||||
* 反复重建直至放弃)
|
||||
* 4. session_dead **不在此清零**(与 net_relisten_locked 同一契约:
|
||||
* 重建监听 ≠ 应用已确认会话结束;清零只发生在 handle_connect_event)
|
||||
*/
|
||||
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 = net_get_sock(i);
|
||||
/* 芯片侧状态两字节 */
|
||||
uint8_t st[2] = {0, 0};
|
||||
/* 芯片当前是否在 LISTEN */
|
||||
int chip_listening;
|
||||
|
||||
/* 控制块无效:跳过 */
|
||||
if (p_sock == NULL) {
|
||||
s_bad[i] = 0U;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 认领门槛:仅监听型 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_LISTENER_STABLE_N) {
|
||||
s_bad[i]++;
|
||||
}
|
||||
/* 去抖未满或处于冷却期:本轮不动手 */
|
||||
if ((s_bad[i] < NET_LISTENER_STABLE_N) ||
|
||||
((int32_t)(now - s_cool[i]) < 0)) {
|
||||
continue;
|
||||
}
|
||||
s_bad[i] = 0U;
|
||||
s_cool[i] = now + NET_LISTENER_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_listener_try(i);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 模块名称:Network Listener Lifecycle (Header)
|
||||
* 模块功能:监听器生命周期模块的对外接口(**真机专属**)—— 维持 fd 处于
|
||||
* LISTEN 态的全部机制:重建、重建前的沉降/隔离(Trap 23)、排空 RX
|
||||
* 残渣、D10 非阻塞重试、周期性自愈 pump。
|
||||
* 适用平台:STM32F407ZGTx + CH395F 以太网芯片
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-10-12
|
||||
* 修改记录:
|
||||
* 2026-10-12 王建锋 从 net_socket.c 抽出监听生命周期(N-32 §10 模块化)
|
||||
*
|
||||
* 职责边界(详见 docs/子系统设计/网络Socket监听生命周期设计说明.md §10):
|
||||
* 本模块只负责「让一个监听型 fd 保持/回到 LISTEN 态」。
|
||||
*
|
||||
* **不属于本模块**(留在 net_socket.c):
|
||||
* - `net_listen_locked`(首次监听的**应用 API 路径**,含 BOUND/CREATED 状态门与缓冲)
|
||||
* - `net_accept_locked`(产出的是**连接**:LISTENING→ESTABLISHED 迁移)
|
||||
* - `net_lost_connect_probe`(做的是**补 accept**,即连接建立)
|
||||
* - `net_listener_armed` / `net_resync_listen`(双端公开 API,sim 也实现)
|
||||
*
|
||||
* ⚠ 本头文件**只被 net_socket.c include**(内部头),不进公开 net_socket.h ——
|
||||
* 故模拟器(simulator/sim/net_socket_sim.c)对本模块**零依赖**,CMakeLists 不编本文件。
|
||||
*/
|
||||
#ifndef NET_LISTENER_H
|
||||
#define NET_LISTENER_H
|
||||
|
||||
#include "net_types.h"
|
||||
|
||||
/*
|
||||
* 沉降/隔离等级:重建监听前"清芯片收尾状态"的强度
|
||||
* (替代此前外部直接写 s_relisten_quarantine[])
|
||||
* QUARANTINE - 异常释放(芯片 TIMEOUT / 对端 RST):对端可能仍有在途数据,
|
||||
* 须排空残渣 + 最短等 400 ms
|
||||
* GRACEFUL - 正常释放(芯片 DISCONNECT / 对端 FIN):无在途数据,
|
||||
* 清理 + 最短等 100 ms(等芯片完成上一条连接的内部收尾)
|
||||
*/
|
||||
typedef enum {
|
||||
NET_LISTENER_SETTLE_QUARANTINE = 1, /* 异常释放:隔离 400 ms + 排空 RX 残渣 */
|
||||
NET_LISTENER_SETTLE_GRACEFUL = 2, /* 正常释放:沉降 100 ms */
|
||||
} net_listener_settle_t;
|
||||
|
||||
/*
|
||||
* 函数功能:登记「本 fd 的会话已结束,重建监听前需沉降/隔离」
|
||||
* 入口参数:sockfd - Socket 描述符 (0~7) int
|
||||
* level - 沉降等级(释放来源决定,见 net_listener_settle_t)
|
||||
* 出口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:netTask 上下文;由 handle_disconnect_event / handle_timeout_event 调用
|
||||
* 函数说明:1. 只置事实,不做动作 —— 重建由 net_listener_pump / net_listener_try 负责
|
||||
* 2. 越界 sockfd 静默忽略(不设错误码,与旧直接写数组行为一致)
|
||||
*/
|
||||
void net_listener_settle(int sockfd, net_listener_settle_t level);
|
||||
|
||||
/*
|
||||
* 函数功能:尝试重建监听一次(**不阻塞**);失败则登记"待重试"
|
||||
* 入口参数:sockfd - Socket 描述符 (0~7) int
|
||||
* 出口参数:无
|
||||
* 返回值:0 - 成功(监听器已就绪);-1 - 本次失败或已在重试队列中 int
|
||||
* 限定条件:netTask 上下文;fd 为监听型(auto_relisten)
|
||||
* 函数说明:1. 已在重试队列时直接返回 -1(不重复尝试、不重置重试时刻)——
|
||||
* 否则每 10 ms 一次的 accept 轮询会不断把重试时刻往后推,
|
||||
* 导致永不重试(重试饥饿)
|
||||
* 2. 替代此前散落在 net_socket.c 各处的 net_relisten_try()
|
||||
*/
|
||||
int net_listener_try(int sockfd);
|
||||
|
||||
/*
|
||||
* 函数功能:清除本 fd 的待重试/沉降状态(关闭或新建 fd 时调用)
|
||||
* 入口参数:sockfd - Socket 描述符 (0~7) int
|
||||
* 出口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:无(越界静默返回)
|
||||
* 函数说明:防同 fd 下一生成继承上轮的重试队列或 400/800 ms 隔离等待
|
||||
*/
|
||||
void net_listener_clear(int sockfd);
|
||||
|
||||
/*
|
||||
* 函数功能:D10 重试轮询(netTask 每轮调用,非阻塞)
|
||||
* 入口参数:无
|
||||
* 出口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:netTask 上下文(由 net_process_messages 每轮调用)
|
||||
* 函数说明:到点重试待办的重监听;成功即报告探针样本;超时放弃并记错误日志
|
||||
*/
|
||||
void net_listener_retry_tick(void);
|
||||
|
||||
/*
|
||||
* 函数功能:监听器 pump —— 本层**唯一**的周期性重建决策者
|
||||
* 入口参数:无
|
||||
* 出口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:netTask 上下文(由 net_process_messages 每轮调用)
|
||||
* 函数说明:1. 覆盖三种情形:① LISTENING 而芯片失位 → 重建
|
||||
* ② CLOSED 而芯片不在 LISTEN → 重建
|
||||
* ③ CLOSED 而芯片**正在** LISTEN → 只做状态重同步(U11,不发芯片命令)
|
||||
* 2. 100 ms 周期 + 去抖 + 冷却(详见实现处注释)
|
||||
*/
|
||||
void net_listener_pump(void);
|
||||
|
||||
/* 注:本模块消费的宿主能力 `net_pending_active()` 声明在 net_socket.h
|
||||
* (它是 net_socket.c 的函数,须与配对头文件同处,见规范 S17 2.2)。 */
|
||||
|
||||
#endif /* NET_LISTENER_H */
|
||||
+37
-494
@@ -18,6 +18,7 @@
|
||||
#include "cmsis_os.h"
|
||||
|
||||
#include "net_socket.h"
|
||||
#include "net_listener.h"
|
||||
#include "ch395f.h"
|
||||
#include "test_config.h" /* TEST_LATENCY_PROBE(链路时延探针编译开关) */
|
||||
#include "net_latency_test.h" /* L3 探针接口(chunk_wait 采样) */
|
||||
@@ -140,16 +141,15 @@ static uint32_t net_errno_slot(void) {
|
||||
* 出口参数:无
|
||||
* 返回值:err 原值(便于写成 `return net_set_errno(NET_ERR_XXX);`)
|
||||
* 限定条件:无
|
||||
* 函数说明:只写本任务的槽位,其他任务读不到、也改不了 → 消除跨任务覆写竞态
|
||||
* 函数说明:1. 只写本任务的槽位,其他任务读不到、也改不了 → 消除跨任务覆写竞态
|
||||
* 2. N-32 §10 模块化:由 static 提升为**模块间可见**(net_listener.c
|
||||
* 也需要设 errno),声明与 NET_ERR_RETURN_1 宏见 net_socket.h
|
||||
*/
|
||||
static int net_set_errno(int err) {
|
||||
int net_set_errno(int err) {
|
||||
s_net_errno_slots[net_errno_slot()].err = err;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* 「记本任务错误码 + 统一失败返回 -1」的紧凑写法,取代原 `s_net_errno = X; return -1;` */
|
||||
#define NET_ERR_RETURN_1(err) (net_set_errno(err), -1)
|
||||
|
||||
|
||||
|
||||
/*
|
||||
@@ -168,11 +168,6 @@ static int net_send_locked(net_sock_t *p_sock, const void *buf, int len);
|
||||
static int net_recv_locked(net_sock_t *p_sock, void *buf, int len, int flags,
|
||||
uint32_t timeout_ms, TaskHandle_t caller);
|
||||
static int net_close_locked(net_sock_t *p_sock);
|
||||
static int net_relisten_locked(net_sock_t *p_sock);
|
||||
/* D10 非阻塞重监听:单次尝试 + 延迟重试(实现见 net_relisten_locked 之后) */
|
||||
static void net_relisten_clear(int sockfd);
|
||||
static int net_relisten_try(net_sock_t *p_sock);
|
||||
static void net_relisten_retry_tick(void);
|
||||
static int net_accept_locked(int sockfd, struct net_sockaddr *addr, int *addrlen);
|
||||
static int net_connect_locked(int sockfd, const struct net_sockaddr *addr, int addrlen);
|
||||
|
||||
@@ -645,118 +640,6 @@ static void net_reconcile_tick(void) {
|
||||
}
|
||||
}
|
||||
|
||||
/* 监听器 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 宿主 + 消息队列分派器。每轮
|
||||
@@ -792,7 +675,7 @@ void net_process_messages(void) {
|
||||
|
||||
/* 三个非阻塞自愈 tick 置于消息处理之前:断连后即使无应用消息,监听器也能自主恢复 */
|
||||
/* D10 重监听重试(Trap 09):沉降后重开芯片监听器 */
|
||||
net_relisten_retry_tick();
|
||||
net_listener_retry_tick();
|
||||
/* L3 挂起 recv 完成检查:数据到达 / 到点 / 断连时通知调用方 */
|
||||
net_pending_tick();
|
||||
/* Trap 22 状态对账:周期回读芯片状态,自愈丢失的中断(①②;③④ 已归 pump) */
|
||||
@@ -913,16 +796,17 @@ void net_process_messages(void) {
|
||||
break;
|
||||
}
|
||||
case NET_MSG_RELISTEN: {
|
||||
/* 取控制块;NULL = fd 越界或已关闭 */
|
||||
net_sock_t *p_sock = net_get_sock(msg.sockfd);
|
||||
if (p_sock == NULL) {
|
||||
/* 描述符有效性检查(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_relisten_try(p_sock);
|
||||
msg.result = net_listener_try(msg.sockfd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1052,7 +936,7 @@ int net_socket(int domain, int type) {
|
||||
* 消费侧连数据都收不到)——与 released_abnormal 同理,显式清零 */
|
||||
p_sock->session_dead = 0;
|
||||
/* 新 fd 不得继承上一次的待重试状态(D10) */
|
||||
net_relisten_clear(sockfd);
|
||||
net_listener_clear(sockfd);
|
||||
/* 新 fd 不得继承上一次挂起的 recv 请求(L3) */
|
||||
net_pending_clear(sockfd);
|
||||
|
||||
@@ -1438,7 +1322,7 @@ static int net_accept_locked(int sockfd, struct net_sockaddr *addr, int *addrlen
|
||||
* (消费者正在等新连接),不是周期性抢先;删掉会把 CLOSED 窗口
|
||||
* 拉长、导致调用方高频空转 accept(实测 [WD] task stall)。
|
||||
* 非阻塞:失败由 D10 重试轮询接管 */
|
||||
(void)net_relisten_try(p_listen_sock);
|
||||
(void)net_listener_try(sockfd);
|
||||
}
|
||||
}
|
||||
if (drain_pending == 0) {
|
||||
@@ -2049,7 +1933,7 @@ static int net_recv_locked(net_sock_t *p_sock, void *buf, int len, int flags,
|
||||
* `[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);
|
||||
(void)net_listener_try(sockfd);
|
||||
}
|
||||
/* EOF:返回 0(非错误) */
|
||||
return 0;
|
||||
@@ -2074,365 +1958,6 @@ static int net_recv_locked(net_sock_t *p_sock, void *buf, int len, int flags,
|
||||
* 函数说明:1. 调用 ch395f_close_socket 关闭 CH395F 硬件 Socket
|
||||
* 2. 清空整个控制块(memset 0),释放该 Socket 资源
|
||||
*/
|
||||
/* Trap 23(2026-09-23 生产路径实测):异常释放(芯片 TIMEOUT,例如客户端中止上传)
|
||||
* 后的**隔离期**。实测时序:中止时对端仍有在途数据 → 芯片 TIMEOUT(此时 MCU 侧 recv=0)
|
||||
* 在途段随后落进芯片 RX 缓冲(约 2KB 残渣)
|
||||
* 若立即 open+listen,残渣被下一条连接当作自己的数据读走
|
||||
* 新上传只收 2KB 就被判定结束、回报成功(静默截断)
|
||||
* 处置:异常释放的 socket 先隔离 NET_RELISTEN_QUARANTINE_MS:期间持续排空 RX、主动 close 并排空中断
|
||||
* 等芯片回到 CLOSED/TCP_CLOSED;隔离期满才 open+listen
|
||||
* 上限 NET_RELISTEN_SETTLE_MS 后强制放行,保证监听器不会永久失效
|
||||
* 正常 FIN 释放(handle_disconnect_event)不走此路径——既有行为零改动
|
||||
* 声明须早于 net_relisten_locked(其余重试状态见其后区块)
|
||||
*/
|
||||
#define NET_RELISTEN_QUARANTINE_MS 400U /* 异常释放最短隔离(等在途数据落尽 + 排空残渣) */
|
||||
#define NET_RELISTEN_GRACEFUL_MS 100U /* 正常释放最短沉降(等芯片完成上一条连接的收尾) */
|
||||
#define NET_RELISTEN_SETTLE_MS 800U /* 隔离上限(须 < NET_RELISTEN_DEADLINE_MS,
|
||||
* 否则隔离耗尽重试预算 → 监听被放弃,2026-09-23 实测) */
|
||||
/* 重监听前"清芯片收尾状态"的两个等级(s_relisten_quarantine 取值)
|
||||
* QUARANTINE - 异常释放(芯片 TIMEOUT):对端可能仍有在途数据,须清理 + 最短等 400ms
|
||||
* GRACEFUL - 正常释放(芯片 DISCONNECT):无在途数据,清理 + 最短等 100ms
|
||||
* 2026-09-24 台架实证:正常释放走的是既有"不复位芯片收尾状态"的路径
|
||||
* 残留的 DISCONNECT 会在复用同一 fd 的下一条连接上被投递(accept 后约 10~20ms)
|
||||
* 下一条上传被静默截断到 2048B(一个芯片 RX 缓冲)且回报 226
|
||||
* 只清理不等待(0ms)实测无效(4/4 仍截断):芯片需要时间完成上一条连接的内部收尾
|
||||
* 给足沉降即 4/4 通过
|
||||
*/
|
||||
#define NET_RELISTEN_SETTLE_QUARANTINE 1U
|
||||
#define NET_RELISTEN_SETTLE_GRACEFUL 2U
|
||||
static uint8_t s_relisten_quarantine[NET_MAX_SOCKETS]; /* 0=免清;1=异常释放;2=正常释放 */
|
||||
static uint32_t s_relisten_settle_start[NET_MAX_SOCKETS]; /* 隔离起点(0 = 未开始) */
|
||||
static uint32_t s_relisten_settle_dead[NET_MAX_SOCKETS]; /* 隔离上限到期时刻 */
|
||||
|
||||
/*
|
||||
* 函数功能:清空芯片该 socket 的 RX 残留(Trap 23)
|
||||
* 入口参数:sockfd - Socket 描述符 (0~7) int
|
||||
* 出口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:netTask 上下文;socket 即将 close/open(数据已无意义,丢弃)
|
||||
* 函数说明:1. 中止/异常断开时芯片 RX 缓冲可能残留上一条连接的数据(实测残留约一个缓冲 = 2048B)
|
||||
* 2. 残渣带入下一条连接会使其在约 2KB 处被芯片拆链(上传被静默截断)
|
||||
* 3. 关闭前主动读空即清除;分块读避免新增静态缓冲
|
||||
* (2026-10-09 实测:ARMCC 把本 scratch 与同函数内生命周期不重叠的
|
||||
* 192B 日志缓冲叠放,局部缓冲边际栈成本仅 ~16B,静态化反亏 256B BSS,勿再"优化")
|
||||
*/
|
||||
static void net_drain_chip_rx(int sockfd) {
|
||||
/* 复用分块缓冲(256B/块,避免新增静态缓冲) */
|
||||
uint8_t scratch[256];
|
||||
|
||||
/* 上限 16×256 = 4KB(缓冲仅 2KB),防死循环 */
|
||||
for (int guard = 0; guard < 16; guard++) {
|
||||
/* 查询芯片 RX 可用字节数 */
|
||||
int n = (int)ch395f_get_recv_len((uint8_t)sockfd);
|
||||
/* 已排空 → 结束 */
|
||||
if (n <= 0) {
|
||||
break;
|
||||
}
|
||||
/* 单块以缓冲容量为上限 */
|
||||
if (n > (int)sizeof(scratch)) {
|
||||
n = (int)sizeof(scratch);
|
||||
}
|
||||
/* 丢弃该块(读即清) */
|
||||
ch395f_read_recv_buf((uint8_t)sockfd, scratch, (uint16_t)n);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:单连接模式断连后自动重新进入 LISTEN(不清空控制块,保留 fd)
|
||||
* 入口参数:p_sock - 已断开(CLOSED)的 TCP 监听型 Socket 控制块 net_sock_t *
|
||||
* 出口参数:无
|
||||
* 返回值:0 - 成功,-1 - 失败
|
||||
* 限定条件:netTask 上下文;p_sock->in_use == 1;保留 local_port/auto_relisten
|
||||
* 函数说明:1. 复用原本地端口与协议,重新 OPEN + TCP_LISTEN
|
||||
* 2. 仅将 state 置回 LISTENING,不 memset(fd 与 auto_relisten 保持不变)
|
||||
* 3. 实现 BSD 风格"监听 Socket 常驻",应用层无需手动重监听
|
||||
* 4. **单次尝试**:失败立即返回(不 osDelay 忙等)
|
||||
* 5. Trap 09 沉降交给 net_relisten_try / net_relisten_retry_tick 非阻塞重试(见下)
|
||||
* 6. 旧实现在此 osDelay(500) 忙等,netTask 冻结 500ms、实测恢复 588ms(M3 台架 D10)
|
||||
*/
|
||||
static int net_relisten_locked(net_sock_t *p_sock) {
|
||||
/* 芯片命令返回状态 */
|
||||
uint8_t status;
|
||||
/* 由控制块指针反推描述符 */
|
||||
int sockfd = (int)(p_sock - s_net_socks);
|
||||
|
||||
/* 释放隔离期(Trap 23):重监听前先清芯片收尾状态 */
|
||||
if (s_relisten_quarantine[sockfd] != 0U) {
|
||||
/* 芯片状态两字节:[0]=Socket,[1]=TCP */
|
||||
uint8_t st[2] = {0, 0};
|
||||
/* 当前 tick */
|
||||
uint32_t now = HAL_GetTick();
|
||||
/* 隔离等级(1=异常释放 2=正常释放) */
|
||||
uint8_t lvl = s_relisten_quarantine[sockfd];
|
||||
/* 最短等待由释放来源决定(NET_RELISTEN_SETTLE_*):异常释放 400ms(等在途数据落尽)
|
||||
* 正常释放 100ms(等芯片内部收尾) */
|
||||
uint32_t hold = (lvl == NET_RELISTEN_SETTLE_QUARANTINE) ? NET_RELISTEN_QUARANTINE_MS
|
||||
: NET_RELISTEN_GRACEFUL_MS;
|
||||
/* 日志标签(台架分辨触发路径) */
|
||||
const char *tag = (lvl == NET_RELISTEN_SETTLE_QUARANTINE) ? "quarantine" : "settle";
|
||||
|
||||
/* 首次进入隔离期:记录起点与上限 */
|
||||
if (s_relisten_settle_start[sockfd] == 0U) {
|
||||
/* 隔离起点 = 本 tick */
|
||||
s_relisten_settle_start[sockfd] = now;
|
||||
/* 隔离上限 = 起点 + 800ms */
|
||||
s_relisten_settle_dead[sockfd] = now + NET_RELISTEN_SETTLE_MS;
|
||||
}
|
||||
/* 隔离期内持续排空 RX 残渣 */
|
||||
net_drain_chip_rx(sockfd);
|
||||
/* 主动关:清旧连接收尾状态 */
|
||||
ch395f_close_socket((uint8_t)sockfd);
|
||||
/* 读即清:本 Socket 中断状态寄存器 */
|
||||
(void)ch395f_get_sock_int_status((uint8_t)sockfd);
|
||||
/* 读即清:全局中断状态寄存器 */
|
||||
(void)ch395f_get_glob_int_status_all();
|
||||
/* 读芯片当前状态(两字节) */
|
||||
ch395f_get_socket_status((uint8_t)sockfd, st);
|
||||
|
||||
/* 芯片尚未回到 CLOSED/TCP_CLOSED,或未满最短等待 */
|
||||
if (((st[0] != CH395F_SOCKET_CLOSED) || (st[1] != CH395F_TCP_CLOSED) ||
|
||||
((uint32_t)(now - s_relisten_settle_start[sockfd]) < hold))) {
|
||||
/* 未到上限:交重试轮询(30ms 后复检) */
|
||||
if ((int32_t)(now - s_relisten_settle_dead[sockfd]) < 0) {
|
||||
DBG_INFO("sock%d relisten %s (sock=0x%02X tcp=0x%02X)",
|
||||
sockfd, tag, st[0], st[1]);
|
||||
/* 置 BUSY(结果回传调用方) */
|
||||
(void)net_set_errno(NET_ERR_BUSY);
|
||||
/* 交重试轮询(30ms 后复检) */
|
||||
return -1;
|
||||
}
|
||||
/* 隔离上限已至:强制放行(防监听器永久失效) */
|
||||
DBG_INFO("sock%d relisten %s timeout, force open (sock=0x%02X tcp=0x%02X)",
|
||||
sockfd, tag, st[0], st[1]);
|
||||
}
|
||||
/* 隔离完成(或强制放行) */
|
||||
s_relisten_quarantine[sockfd] = 0U;
|
||||
/* 清隔离起点(回到"未开始") */
|
||||
s_relisten_settle_start[sockfd] = 0U;
|
||||
/* 清上限时刻 */
|
||||
s_relisten_settle_dead[sockfd] = 0U;
|
||||
}
|
||||
|
||||
/* 重置协议类型为 TCP */
|
||||
ch395f_set_proto_type((uint8_t)sockfd, CH395F_PROTO_TYPE_TCP);
|
||||
/* 本地端口(沿用原监听) */
|
||||
ch395f_set_sour_port((uint8_t)sockfd, p_sock->local_port);
|
||||
|
||||
/* 打开芯片侧 Socket */
|
||||
status = ch395f_open_socket((uint8_t)sockfd);
|
||||
/* open 失败 → BUSY(Trap 09:芯片需沉降时间才能重开) */
|
||||
if (status != CH395F_ERR_SUCCESS) {
|
||||
/* Trap 09:断开后芯片需沉降时间才能重新 open。此处只清理并返回
|
||||
* (严禁 osDelay 忙等)→ 由重试轮询到点再来。 */
|
||||
/* 先关闭,交重试轮询(30ms 后复检) */
|
||||
ch395f_close_socket((uint8_t)sockfd);
|
||||
/* 置 BUSY(结果回传调用方) */
|
||||
(void)net_set_errno(NET_ERR_BUSY);
|
||||
/* 交重试轮询(30ms 后复检) */
|
||||
return -1;
|
||||
}
|
||||
/* 下发 TCP LISTEN 命令 */
|
||||
status = ch395f_tcp_listen((uint8_t)sockfd);
|
||||
/* LISTEN 失败 → BUSY(同样交重试) */
|
||||
if (status != CH395F_ERR_SUCCESS) {
|
||||
/* 置 BUSY(结果回传调用方) */
|
||||
return NET_ERR_RETURN_1(NET_ERR_BUSY);
|
||||
}
|
||||
|
||||
/* 状态置回 LISTENING(fd 保留) */
|
||||
p_sock->state = NET_SOCK_STATE_LISTENING;
|
||||
/* N-32(P1,设计文档 §5 P1.1 的关键约定):此处**刻意不清 session_dead**。
|
||||
* 重建监听只是"芯片侧恢复就位",与"应用是否已确认上一条会话结束"是两件事;
|
||||
* 清了它,抢先 relisten 之后消费者就再也得不到 EOF(B10"消费者卡死"原型)。
|
||||
* session_dead 只由新 CONNECT 成功(handle_connect_event)或 memset
|
||||
* (net_close/net_socket)清零。 */
|
||||
/* 新监听不得继承上一条连接的释放原因(Trap 23 待办 #3) */
|
||||
p_sock->released_abnormal = 0U;
|
||||
/* N-30(Trap 25):清零前把对端端口转登为幽灵甄别基准 */
|
||||
p_sock->last_peer_port = p_sock->remote_port;
|
||||
/* N-30:新监听会话,丢中断去抖计数清零 */
|
||||
p_sock->lost_conn_seen = 0U;
|
||||
/* 清对端端口(新连接待用) */
|
||||
p_sock->remote_port = 0;
|
||||
/* 清对端 IP */
|
||||
p_sock->remote_ip = 0;
|
||||
/* 清对端 IP 数组 */
|
||||
memset(p_sock->remote_ip_arr, 0, sizeof(p_sock->remote_ip_arr));
|
||||
/* 新开 Socket 发送缓冲按占用计(由 SEND_OK 清除) */
|
||||
p_sock->send_ready = 0;
|
||||
/* 清接收字节统计(新连接) */
|
||||
p_sock->recv_bytes = 0;
|
||||
/* auto_relisten 保持为 1,下次断连仍自动重监听 */
|
||||
|
||||
/* 读即清:仅本 Socket 的中断状态(防上一条会话残留误触发)。
|
||||
* 原为"全 8 个 Socket + 全局"读清,与 net_listen_locked 同源(B3,机制
|
||||
* 与 git 考古见该处注释):本处是每次断连 auto-relisten 的热路径,全量清
|
||||
* 会持续吞掉其它在用 Socket 的 CONNECT/DISCONNECT 等边沿事件。全局中断
|
||||
* 寄存器交 net_poll 集中读清(AGENTS 模块约定)。 */
|
||||
(void)ch395f_get_sock_int_status((uint8_t)sockfd);
|
||||
|
||||
/* 留痕:监听器重建完成 */
|
||||
DBG_INFO("sock%d auto-relisten ok", sockfd);
|
||||
/* 重监听成功 */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ===== Trap 09 非阻塞重监听重试(D10 修复,2026-09-22 M3 实测) =====
|
||||
* 旧实现:open 失败 → close → osDelay(500) → 重试,netTask 忙等 500ms(全网冻结)
|
||||
* 且实测恢复 588ms、期间客户端重连被拒
|
||||
* 新实现:首次失败即登记待重试,由 netTask 每轮 net_process_messages() 到点重试
|
||||
* 恢复时间 ≈ NET_RELISTEN_RETRY_MS 量级,任何时刻都不阻塞任务
|
||||
*/
|
||||
#define NET_RELISTEN_RETRY_MS 30U /* 重试间隔(Trap 09 沉降窗口;实测 100ms 首次必成,
|
||||
* 收紧到 30ms 以缩短恢复时间——未成则下一轮到点再来) */
|
||||
#define NET_RELISTEN_DEADLINE_MS 2000U /* 放弃时限(自首次尝试起) */
|
||||
|
||||
static uint8_t s_relisten_wait[NET_MAX_SOCKETS]; /* 1 = 有待重试的重监听 */
|
||||
static uint32_t s_relisten_next[NET_MAX_SOCKETS]; /* 下次重试时刻(tick) */
|
||||
static uint32_t s_relisten_dead[NET_MAX_SOCKETS]; /* 放弃时刻(tick) */
|
||||
#if defined(TEST_LATENCY_PROBE)
|
||||
static lat_tmark_t s_relisten_mark[NET_MAX_SOCKETS]; /* 恢复计时起点(首次尝试) */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 函数功能:清除本 fd 的待重监听重试状态(关闭/新建 fd 时调用)
|
||||
* 入口参数:sockfd - Socket 描述符 (0~7)
|
||||
* 出口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:无
|
||||
* 函数说明:1. 清"待重试"标志与重试时刻,fd 关闭/新建时调用(防残留重试)
|
||||
*/
|
||||
static void net_relisten_clear(int sockfd) {
|
||||
/* 描述符越界:直接返回(本函数不设错误码) */
|
||||
if ((sockfd < 0) || (sockfd >= NET_MAX_SOCKETS)) {
|
||||
return;
|
||||
}
|
||||
/* 清"待重试"标志 */
|
||||
s_relisten_wait[sockfd] = 0U;
|
||||
/* 清下次重试时刻 */
|
||||
s_relisten_next[sockfd] = 0U;
|
||||
/* 清放弃时刻 */
|
||||
s_relisten_dead[sockfd] = 0U;
|
||||
/* Trap 23:一并清沉降/隔离状态 */
|
||||
s_relisten_settle_dead[sockfd] = 0U;
|
||||
/* 清沉降起点 */
|
||||
s_relisten_settle_start[sockfd] = 0U;
|
||||
/* 一并清隔离标志:关闭/重建即确定性释放,
|
||||
* 防同 fd 下一生成继承上轮的 400/800ms
|
||||
* 隔离等待(2026-09-24 台架发现跨轮残留) */
|
||||
s_relisten_quarantine[sockfd] = 0U;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:标记重监听完成(清重试状态;探针构建下报告恢复计时样本)
|
||||
* 入口参数:sockfd - Socket 描述符 (0~7)
|
||||
* 出口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:netTask 上下文
|
||||
* 函数说明:1. 重监听成功收尾:清重试状态;探针构建(TEST_LATENCY_PROBE)下上报恢复计时样本
|
||||
*/
|
||||
static void net_relisten_done(int sockfd) {
|
||||
/* 清重试状态(复用清除函数) */
|
||||
net_relisten_clear(sockfd);
|
||||
#if defined(TEST_LATENCY_PROBE)
|
||||
/* 上报恢复计时样本(首次尝试 → 成功) */
|
||||
lat_probe_add_seg("relisten", lat_mark_us(&s_relisten_mark[sockfd]));
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:尝试重监听一次;失败则登记"待重试"(**不阻塞** netTask)
|
||||
* 入口参数:p_sock - CLOSED 的监听型 Socket 控制块 net_sock_t *
|
||||
* 出口参数:无
|
||||
* 返回值:0 - 成功(监听器已就绪);-1 - 本次失败或已在重试队列中
|
||||
* 限定条件:netTask 上下文
|
||||
* 函数说明:已有待重试时直接返回 -1(不重复尝试、不重置重试时刻)——否则每 10ms
|
||||
* 一次的 accept 轮询会不断把重试时刻往后推,导致永不重试(饥饿)。
|
||||
*/
|
||||
static int net_relisten_try(net_sock_t *p_sock) {
|
||||
/* 由控制块指针反推描述符 */
|
||||
int sockfd = (int)(p_sock - s_net_socks);
|
||||
/* 当前 tick */
|
||||
uint32_t now = HAL_GetTick();
|
||||
|
||||
/* 描述符越界 → BADF */
|
||||
if ((sockfd < 0) || (sockfd >= NET_MAX_SOCKETS)) {
|
||||
return NET_ERR_RETURN_1(NET_ERR_BADF);
|
||||
}
|
||||
/* 已在重试队列:不重复尝试、不重置时刻(防重试饥饿) */
|
||||
if (s_relisten_wait[sockfd] != 0U) {
|
||||
(void)net_set_errno(NET_ERR_BUSY);
|
||||
/* 已在重试队列:由 tick 负责 */
|
||||
return -1;
|
||||
}
|
||||
#if defined(TEST_LATENCY_PROBE)
|
||||
/* 恢复计时自首次尝试起 */
|
||||
lat_mark(&s_relisten_mark[sockfd]);
|
||||
#endif
|
||||
/* 本次尝试成功 */
|
||||
if (net_relisten_locked(p_sock) == 0) {
|
||||
/* 标记完成(清状态 + 探针样本) */
|
||||
net_relisten_done(sockfd);
|
||||
/* 监听器已就绪 */
|
||||
return 0;
|
||||
}
|
||||
/* 登记"待重试" */
|
||||
s_relisten_wait[sockfd] = 1U;
|
||||
/* 下次重试时刻 = now + 30ms */
|
||||
s_relisten_next[sockfd] = now + NET_RELISTEN_RETRY_MS;
|
||||
/* 放弃时刻 = now + 2s */
|
||||
s_relisten_dead[sockfd] = now + NET_RELISTEN_DEADLINE_MS;
|
||||
/* 本次失败(重试交 tick) */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:重试轮询(netTask 每轮调用一次,非阻塞)
|
||||
* 入口参数:无
|
||||
* 出口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:netTask 上下文(net_process_messages 开头调用)
|
||||
* 函数说明:到点重试待办的自动重监听;成功即报告探针样本;超时放弃并记错误日志
|
||||
*/
|
||||
static void net_relisten_retry_tick(void) {
|
||||
/* 当前 tick */
|
||||
uint32_t now = HAL_GetTick();
|
||||
/* Socket 循环索引 */
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NET_MAX_SOCKETS; i++) {
|
||||
/* 本 fd 无待重试重监听:跳过 */
|
||||
if (s_relisten_wait[i] == 0U) {
|
||||
continue;
|
||||
}
|
||||
/* 本 fd 有在途挂起 recv:重开 Socket 会摧毁在途连接(L3 保险) */
|
||||
if (s_net_pending[i].active != 0U) {
|
||||
continue;
|
||||
}
|
||||
/* 未到点(tick 差值按有符号比较,抗回绕) */
|
||||
if ((int32_t)(now - s_relisten_next[i]) < 0) {
|
||||
continue;
|
||||
}
|
||||
/* 已到放弃时限 */
|
||||
if ((int32_t)(now - s_relisten_dead[i]) >= 0) {
|
||||
/* 清重试状态 */
|
||||
net_relisten_clear(i);
|
||||
/* 放弃并记错误日志 */
|
||||
DBG_ERROR("sock%d relisten give up (Trap 09 not cleared)", i);
|
||||
continue;
|
||||
}
|
||||
/* 重试成功 */
|
||||
if (net_relisten_locked(&s_net_socks[i]) == 0) {
|
||||
/* 标记完成(清状态 + 探针样本) */
|
||||
net_relisten_done(i);
|
||||
/* 留痕:重试成功 */
|
||||
DBG_INFO("sock%d relisten retry ok", i);
|
||||
} else {
|
||||
/* 仍未成:置下次重试时刻 */
|
||||
s_relisten_next[i] = now + NET_RELISTEN_RETRY_MS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== L3 挂起 recv 请求的实现(一期,2026-09-22) =====
|
||||
* 完成条件按 ①数据 ②到点 ③断连 顺序检查(逐项对齐旧自旋循环语义)
|
||||
@@ -2631,8 +2156,8 @@ static void net_pending_tick(void) {
|
||||
* 消费者驱动路径,不是周期性抢先(同 net_recv_locked 的理由)。 */
|
||||
if ((p_sock->state == NET_SOCK_STATE_CLOSED) &&
|
||||
(p_sock->auto_relisten != 0U)) {
|
||||
/* 非阻塞:失败由 D10 重试轮询接管 */
|
||||
(void)net_relisten_try(p_sock);
|
||||
/* 非阻塞:失败由 D10 重试轮询接管(N-32 §10:入口收敛到 net_listener_try) */
|
||||
(void)net_listener_try(i);
|
||||
}
|
||||
/* Trap 23 待办 #3:异常释放(TIMEOUT/RST)不是正常 EOF(对端未发正常 FIN)
|
||||
* → 以连接重置了结:应用(lftpd STOR)对异常结束回 5xx 而非误报 226;
|
||||
@@ -2662,6 +2187,24 @@ static void net_pending_tick(void) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:查询本 fd 是否有在途挂起 recv(L3 pending)—— 宿主能力,供 net_listener 用
|
||||
* 入口参数:sockfd - Socket 描述符 (0~7) int
|
||||
* 出口参数:无
|
||||
* 返回值:1 - 有挂起;0 - 无(含描述符越界) int
|
||||
* 限定条件:任意上下文(纯 RAM 读,不发起 RPC)
|
||||
* 函数说明:1. N-32 §10 模块化:net_listener_retry_tick 用它跳过"重开 Socket
|
||||
* 会摧毁在途连接"的 fd(L3 保险,原为直接读 s_net_pending[i].active)
|
||||
* 2. 声明在 net_listener.h(本模块提供、监听器模块消费)
|
||||
*/
|
||||
int net_pending_active(int sockfd) {
|
||||
/* 描述符越界:视为无挂起 */
|
||||
if ((sockfd < 0) || (sockfd >= NET_MAX_SOCKETS)) {
|
||||
return 0;
|
||||
}
|
||||
return (s_net_pending[sockfd].active != 0U) ? 1 : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:清除本 fd 的挂起请求(不通知;用于"新 fd 不得继承"与 close 后的幂等清理)
|
||||
* 入口参数:sockfd - Socket 描述符 (0~7) int
|
||||
@@ -2806,7 +2349,7 @@ static int net_close_locked(net_sock_t *p_sock) {
|
||||
memset(p_sock, 0, sizeof(net_sock_t));
|
||||
p_sock->last_peer_port = keep_peer;
|
||||
/* 关闭即取消该 fd 的待重试(D10 重试队列) */
|
||||
net_relisten_clear(sockfd);
|
||||
net_listener_clear(sockfd);
|
||||
/* 关闭即清除该 fd 的挂起状态(L3;幂等) */
|
||||
net_pending_clear(sockfd);
|
||||
|
||||
@@ -3882,7 +3425,7 @@ static void handle_disconnect_event(net_sock_t *p_sock) {
|
||||
/* 描述符范围保护 */
|
||||
if ((qfd >= 0) && (qfd < NET_MAX_SOCKETS)) {
|
||||
/* 登记优雅沉降(NET_RELISTEN_SETTLE_GRACEFUL) */
|
||||
s_relisten_quarantine[qfd] = NET_RELISTEN_SETTLE_GRACEFUL;
|
||||
net_listener_settle(qfd, NET_LISTENER_SETTLE_GRACEFUL);
|
||||
}
|
||||
}
|
||||
/* 正常释放(对端 FIN):断连了结回 EOF(0) */
|
||||
@@ -3975,7 +3518,7 @@ static void handle_timeout_event(net_sock_t *p_sock) {
|
||||
/* 描述符范围保护 */
|
||||
if ((qfd >= 0) && (qfd < NET_MAX_SOCKETS)) {
|
||||
/* 登记异常释放隔离(重监听前等待在途数据落尽 + RX 排空) */
|
||||
s_relisten_quarantine[qfd] = 1U;
|
||||
net_listener_settle(qfd, NET_LISTENER_SETTLE_QUARANTINE);
|
||||
}
|
||||
}
|
||||
/* Trap 23 待办 #3:标记异常释放——L3 挂起 recv 排空断连后以
|
||||
|
||||
@@ -435,6 +435,34 @@ void net_process_messages(void);
|
||||
*/
|
||||
net_sock_t *net_get_sock(int sockfd);
|
||||
|
||||
/*
|
||||
* 函数功能:记录**本任务**最后一次失败的精确错误码(net 层内部通用工具)
|
||||
* 入口参数:err - 错误码(NET_ERR_xxx,负值)
|
||||
* 出口参数:无
|
||||
* 返回值:err 原值(便于写成 `return net_set_errno(NET_ERR_XXX);`)
|
||||
* 限定条件:无
|
||||
* 函数说明:1. 只写本任务的槽位,其他任务读不到、也改不了 → 消除跨任务覆写竞态
|
||||
* 2. N-32 §10 模块化:由 net_socket.c 的 static 提升为**模块间可见**,
|
||||
* 供 net_listener.c 等同目录内部模块复用(定义仍在 net_socket.c)
|
||||
*/
|
||||
int net_set_errno(int err);
|
||||
|
||||
/* 「记本任务错误码 + 统一失败返回 -1」的紧凑写法(取代 `s_net_errno = X; return -1;`)
|
||||
* N-32 §10:原定义在 net_socket.c,随 net_set_errno 一并提升到本头文件 */
|
||||
#define NET_ERR_RETURN_1(err) (net_set_errno(err), -1)
|
||||
|
||||
/*
|
||||
* 函数功能:查询本 fd 是否有在途挂起 recv(L3 pending)—— 供 net_listener.c 消费
|
||||
* 入口参数:sockfd - Socket 描述符 (0~7) int
|
||||
* 出口参数:无
|
||||
* 返回值:1 - 有挂起;0 - 无(含描述符越界) int
|
||||
* 限定条件:任意上下文(纯 RAM 读,不发起 RPC)
|
||||
* 函数说明:1. N-32 §10 模块化:net_listener_retry_tick 用它跳过"重开 Socket
|
||||
* 会摧毁在途连接"的 fd(L3 保险,原为直接读 s_net_pending[i].active)
|
||||
* 2. 定义在 net_socket.c(配对头即本文件)
|
||||
*/
|
||||
int net_pending_active(int sockfd);
|
||||
|
||||
/*
|
||||
* 函数功能:获取 Socket 本地地址(IP + Port)
|
||||
* 入口参数:sockfd - socket 描述符
|
||||
|
||||
@@ -1553,6 +1553,11 @@
|
||||
<FileType>1</FileType>
|
||||
<FilePath>..\Drivers\BSP\NET\net_socket.c</FilePath>
|
||||
</File>
|
||||
<File>
|
||||
<FileName>net_listener.c</FileName>
|
||||
<FileType>1</FileType>
|
||||
<FilePath>..\Drivers\BSP\NET\net_listener.c</FilePath>
|
||||
</File>
|
||||
<File>
|
||||
<FileName>net_cfg.c</FileName>
|
||||
<FileType>1</FileType>
|
||||
|
||||
@@ -36,10 +36,10 @@
|
||||
<!-- MEM_SNAPSHOT:BEGIN generated by scripts/check_mem_numbers.py -->
|
||||
| 区域 | 用量 | 容量 | 占比 |
|
||||
|---|---|---|---|
|
||||
| ER_IROM1 | 214,248 B (209.23 KB) | 1,048,576 B (1024 KB) | 20.4% |
|
||||
| ER_IROM1 | 214,228 B (209.21 KB) | 1,048,576 B (1024 KB) | 20.4% |
|
||||
| RW_IRAM1 | 94,808 B (92.59 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 合计)** | **215,228 B (210.2 KB)** | 1,048,576 B (1024 KB) | **20.5%** |
|
||||
| **Flash (ROM 合计)** | **215,208 B (210.2 KB)** | 1,048,576 B (1024 KB) | **20.5%** |
|
||||
<!-- MEM_SNAPSHOT:END -->
|
||||
|
||||
各区的容量与常量来源:
|
||||
|
||||
@@ -107,6 +107,7 @@ SIM_EXEMPT = [
|
||||
("Drivers/BSP/NET/lftpd/lftpd_string.c", "MCU FTP 字符串工具"),
|
||||
("Drivers/BSP/NET/net_select.c", "MCU socket 选择层"),
|
||||
("Drivers/BSP/NET/net_socket.c", "MCU socket 后端,模拟器另有 net_socket_sim.c"),
|
||||
("Drivers/BSP/NET/net_listener.c", "MCU 监听器生命周期(依赖 ch395f_* SPI 驱动与 net_socket.c 静态状态),模拟器无对应(Winsock 后端无孤儿态/重监听概念)"),
|
||||
("Drivers/BSP/RS485/rs485.c", "MCU RS-485 驱动"),
|
||||
("Drivers/BSP/SD2506/sd2506.c", "MCU RTC 驱动"),
|
||||
("Drivers/BSP/TPAFE5160/tpafe5160.c", "MCU 计量芯片驱动"),
|
||||
|
||||
Reference in New Issue
Block a user