feat(NET): N34 阶段 1.5 recv 语义主流化——无限默认+具名非阻塞+模式属性

核心语义(实施方案 §3.5):
- net_recv 默认无限阻塞:删除 NET_RECV_TIMEOUT_MS(30s 隐藏默认预算),
  超时归调用方策略;唤醒源=数据/EOF/close/网络重建(CONNRESET),
  会话死亡由对账① ~400ms 判死兜底,无"对端消失永久挂死"窗口
- 新增 net_recv_nb(NET_MSG_DONTWAIT 路径转正具名)与
  net_setblocking(fd,on)(阻塞模式挂控制块属性,对齐 O_NONBLOCK/
  Python setblocking;直写单字节,net_set_event_cb 先例)
- net_types.h net_sock_t 新增 blocking 属性;net_socket() 默认置 1
  (close memset 清零后显式重置,同 released_abnormal 纪律)
- 原 1s 强制了结看门狗降级为 60s tripwire:只 [ERR] 留痕一次,
  绝不伪造 TIMEDOUT 返回值(旧实现掩盖完成路径缺陷)
- net_recvfrom 删 30s 自旋超时、同判据对齐(零生产调用者,注释警示)
- 文档契约:IWDG required_mask 内任务禁止无限阻塞 recv(BSD 指南
  §3.5/§10.1 + net_socket.h 注释)

调用点审计(§3.5.5):零生产调用点采用无限默认——iec104_serv/
ui_net_sink 转 net_recv_nb(受监控任务逐位等价);lftpd/time_sync
已是显式预算 net_recv_to 不动;32 轮盲收兜底未动(阶段 3 E6 门禁)

模拟器双端镜像 net_recv_nb/net_recv_to/net_setblocking(包装层
Sleep(1) 轮询模拟阻塞);差距清单 N-3/N-6 登记义务已履行

门禁与台架:
- @build 0/0:Code 209580→209780(+200B) ZI +32B;RAM 受管区官方
  脚本同步;ci_local L0 32/32 + L0.5 951/951
- 台架全回归:relisten :7003 50/50、iec104 18/18、对时正常、
  ftp_regression 11/11、Trap23 RST 5/5+FIN 5/5、ftp_integrity
  20/20 字节一致、n30 0/10、@test PASS
- 模拟器 build_sim OK + check_remo_sink 黄金参考 0/25600 位差

队友 p15-recv-semantics 实施,Lead 全量审阅 + 独立复跑门禁与回归
This commit is contained in:
2026-10-13 11:23:19 +08:00
parent 8f902e09fd
commit d346e4ee84
17 changed files with 487 additions and 147 deletions
+57 -12
View File
@@ -48,7 +48,8 @@ typedef struct {
void *buf; /* 数据缓冲区(send/recv) */
int len; /* 数据长度/缓冲区大小 */
int flags; /* 接收标志 */
uint32_t timeout_ms; /* 接收超时(入队方向;0 = 用默认 NET_RECV_TIMEOUT_MS) */
uint32_t timeout_ms; /* 接收超时(入队方向;0 = 无限等待,无默认预算——
* N34 阶段 1.5 删除 NET_RECV_TIMEOUT_MS 30s 默认) */
TaskHandle_t caller; /* 调用方任务句柄 */
int result; /* 操作结果 */
/* 连接参数 */
@@ -230,19 +231,55 @@ int net_connect(int sockfd, const struct net_sockaddr *addr, int addrlen);
int net_send(int sockfd, const void *buf, int len, int flags);
/*
* 函数功能:接收数据(TCP)
* 函数功能:接收数据(TCP,**默认无限阻塞**——N34 阶段 1.5 语义主流化)
* 入口参数:sockfd - socket 描述符
* buf - 接收缓冲区
* len - 缓冲区大小
* flags - 通常为 0,可设置 NET_MSG_DONTWAIT 非阻塞
* flags - 通常为 0;NET_MSG_DONTWAIT 单次强制非阻塞
* 返回值:实际接收字节数,0=对端关闭,-1=错误
* 限定条件:TCP 连接已建立
* 函数说明:在阻塞模式下,如果没有数据,函数会等待;
* 在非阻塞模式下,如果没有数据,返回 -1 并设置
* net_errno 为 NET_ERR_WOULDBLOCK
* 函数说明:1. 无数据时**无限等待**(无默认超时预算,对齐 POSIX/Python:模式归
* 使用者、超时归调用方);唤醒源 = 数据 / EOF / close / 网络重建
* (NET_ERR_CONNRESET)——会话死亡由对账①在 ~400ms 内判死兜底,
* 不存在"对端消失后永久挂死"的窗口
* 2. 模式属性:net_setblocking(fd, 0) 后本接口无数据立即返回
* NET_ERR_WOULDBLOCK;NET_MSG_DONTWAIT 仅单次覆盖该属性
* 3. ⚠ 文档契约(嵌入式特有):IWDG required_mask 内任务**禁止无限
* 阻塞 recv**——必须 net_select 超时或 net_recv_to 显式预算,
* 否则"对端连上不发数据"= IWDG 复位(超时归使用者对受监控任务是义务)
* 4. 超时首选 net_select(r,w,e,timeout_ms);net_recv_to 为显式预算便捷入口
*/
int net_recv(int sockfd, void *buf, int len, int flags);
/*
* 函数功能:接收数据(非阻塞具名入口,原 NET_MSG_DONTWAIT 路径转正——N34 阶段 1.5)
* 入口参数:sockfd - socket 描述符
* buf - 接收缓冲区
* len - 缓冲区大小
* 返回值:实际接收字节数;0 = 对端关闭(net 层已自动重监听);
* -1 = 错误(无数据 = NET_ERR_WOULDBLOCK,其余 errno 可查)
* 限定条件:非 netTask 上下文(netTask 内用 net_recv_sock + DONTWAIT)
* 函数说明:等价 net_recv(sockfd, buf, len, NET_MSG_DONTWAIT);
* 不改变 socket 的 blocking 属性(模式用 net_setblocking 设置)
*/
int net_recv_nb(int sockfd, void *buf, int len);
/*
* 函数功能:设置 Socket 阻塞/非阻塞**模式属性**(对齐 BSD O_NONBLOCK / Python
* setblocking——N34 阶段 1.5,模式挂 socket 控制块属性)
* 入口参数:sockfd - socket 描述符
* on - 1 = 阻塞(net_socket 默认);0 = 非阻塞
* 返回值:0 成功;-1 失败(描述符无效/未占用)
* 限定条件:socket 已创建
* 函数说明:1. 属性对 net_recv / net_recv_sock 生效:非阻塞模式下无数据立即
* 返回 NET_ERR_WOULDBLOCK(EOF 判定语义不变:CLOSED/session_dead
* 无数据仍返回 0 并触发自动重监听)
* 2. 直写控制块单字节(先例 net_set_event_cb),任意上下文可调;
* 最坏一次在途 recv 用旧模式,无撕裂风险
* 3. 与超时预算正交:模式是粘滞属性,单次预算用 net_recv_to/net_select
*/
int net_setblocking(int sockfd, int on);
/*
* 函数功能:接收数据(**带调用方指定超时**的阻塞版,用于分片有界读)
* 入口参数:sockfd - Socket 描述符
@@ -250,10 +287,14 @@ int net_recv(int sockfd, void *buf, int len, int flags);
* timeout_ms - 单次调用在 netTask 内的最长等待(毫秒,>0)
* 返回值:>0 实际字节数;0 = 对端关闭(EOF);-1 = 真错误(errno 可查);**-2 = 超时无数据**
* 限定条件:非 netTask 上下文;Socket 已连接
* 函数说明:为"不能长时间占用 netTask"的调用方(如 lftpd 控制行/数据读)提供
* **分片**阻塞:每次调用最多占用 netTask timeout_ms,返回 -2 表示这段
* 时间内无数据,调用方自行累计总预算并决定是否继续(E-02 头阻塞修复)。
* 超时用独立返回码而非 errno → 无跨任务 errno 竞态。
* 函数说明:1. **显式预算**便捷入口(等价 Python settimeout,N34 阶段 1.5 起
* 不再有隐藏默认值):net_recv 的无限阻塞 + 本接口的显式预算构成
* 超时语义的全部来源;超时首选仍是 net_select
* 2. E-02 修复配套:lftpd 等"等对端下一行/下一块数据"的调用方
* 若无限阻塞 recv,会把 netTask(全部 socket 操作的唯一执行者)长期占住
* 3. 用本接口把等待切成 ≤timeout_ms 的片,调用方自行累计总预算:总超时语义不变、单次占用有界
* 4. 超时用独立返回码 -2(而非靠 errno 区分)→ 无跨任务 errno 竞态
* 5. ⚠ IWDG required_mask 内任务必须走本接口或 net_select(见 net_recv 契约)
*/
int net_recv_to(int sockfd, void *buf, int len, uint32_t timeout_ms);
@@ -277,12 +318,16 @@ int net_sendto(int sockfd, const void *buf, int len, int flags,
* 入口参数:sockfd - socket 描述符
* buf - 接收缓冲区
* len - 缓冲区大小
* flags - 通常为 0
* flags - 通常为 0;NET_MSG_DONTWAIT 单次强制非阻塞
* src_addr - 输出:发送方地址
* addrlen - 输入输出:地址长度
* 返回值:实际接收字节数,失败返回 -1
* 限定条件:socket 已创建,指针非空
* 函数说明:读取 CH395F 接收缓冲区,获取对端地址
* 函数说明:1. 读取 CH395F 接收缓冲区,获取对端地址
* 2. 阻塞语义与 net_recv 同(N34 阶段 1.5):blocking 属性下无数据
* 无限等待,DONTWAIT / 非阻塞属性立即 NET_ERR_WOULDBLOCK;
* 本接口为轮询自旋实现(历史遗留,UDP 专用),受 IWDG 监控的
* 任务同样禁止对其无限等待
*/
int net_recvfrom(int sockfd, void *buf, int len, int flags,
struct net_sockaddr *src_addr, int *addrlen);