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:
@@ -11,6 +11,9 @@
|
||||
* 实际生效值 60s/5s/3 并在 net_socket.c 引用(原宏为无效遗留值)
|
||||
* 2026-09-22 新增 NET_RETRANS_COUNT/NET_RETRANS_PERIOD_MS(P0-a:重传周期
|
||||
* 500→200ms 治 D1 症状),net_socket.c 的字面量 8/500 移入本文件
|
||||
* 2026-10-13 N34 阶段 1.5:删除 NET_RECV_TIMEOUT_MS(30s 默认预算)——
|
||||
* recv 超时改为调用方策略(net_recv 无限阻塞 /
|
||||
* net_recv_to 显式预算 / net_select 首选),API 不再强加默认值
|
||||
*/
|
||||
|
||||
#ifndef NET_CONFIG_H
|
||||
@@ -30,15 +33,14 @@ extern "C" {
|
||||
/*
|
||||
* 超时时间配置(毫秒)
|
||||
* 0 = 无限等待(阻塞模式)
|
||||
* 注:recv 侧无编译期默认预算(N34 阶段 1.5 删除 NET_RECV_TIMEOUT_MS)——
|
||||
* 超时是调用方策略:net_recv() 无限阻塞、net_recv_to() 显式预算、
|
||||
* net_select() 首选机制。
|
||||
*/
|
||||
#ifndef NET_SEND_TIMEOUT_MS
|
||||
#define NET_SEND_TIMEOUT_MS 1000 /* 发送超时 */
|
||||
#endif
|
||||
|
||||
#ifndef NET_RECV_TIMEOUT_MS
|
||||
#define NET_RECV_TIMEOUT_MS 30000 /* 接收超时(30秒,0=无限等待) */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TCP 重传配置(由 net_socket.c 的 net_init() 在 ch395f_init() 之前下发)
|
||||
* P0-a(2026-09-22):周期 500→200ms——单次丢包代价 500→200ms(治 D1 症状),
|
||||
|
||||
+161
-60
@@ -174,19 +174,23 @@ static int net_connect_locked(int sockfd, const struct net_sockaddr *addr, int a
|
||||
/* L3 pending 化(一期,E-02 结构演进):阻塞等待 = 挂起请求 + 主循环节拍完成
|
||||
* "无数据 + 阻塞等待"不再自旋占用 netTask:登记 pending 后立即返回
|
||||
* net_pending_tick(与 D10 重监听重试同节奏,net_process_messages 开头每轮调用)
|
||||
* 按 ①数据 ②到点 ③断连 顺序检查完成条件并通知调用方(语义逐项对齐旧自旋循环)
|
||||
* 按 ①数据 ②到点(仅显式预算) ③断连 顺序检查完成条件并通知调用方(语义逐项对齐旧自旋循环)
|
||||
* 关闭/网络重建/陈旧重同步走显式 settle/cancel 路径(取消必通知,调用方永不挂死)
|
||||
* 公开 API 返回契约不变:>0 字节数 / 0 EOF / -1 错误(错误码经通知值回传)
|
||||
* N34 阶段 1.5:预算归调用方——timeout_ms=0 即无限等待(30s 默认预算已删),
|
||||
* 原"到点 +1s 强制了结"的看门狗降级为 tripwire(只留痕、绝不伪造返回值)
|
||||
*/
|
||||
#define NET_PENDING_REGISTERED 0x70000000 /* 内部哨兵:pending 已登记(分派跳过即时通知;取值不与字节数空间冲突) */
|
||||
#define NET_PENDING_WATCHDOG_MS 1000U /* 看门狗:到点后 1s 仍未了结 = 完成逻辑缺位 → 强制了结 + 错误日志 */
|
||||
#define NET_RECV_TRIPWIRE_MS 60000U /* tripwire:挂起超 60s = 完成逻辑缺位探针 → 只 [ERR] 留痕一次,绝不强制了结 */
|
||||
|
||||
typedef struct {
|
||||
uint8_t active; /* 1 = 本 fd 有挂起 recv */
|
||||
uint8_t infinite; /* 1 = 无限等待(无 deadline,N34 阶段 1.5:0 预算 = 永等) */
|
||||
uint8_t tripwire_logged; /* 1 = 本轮挂起已打过 tripwire(保证只留痕一条) */
|
||||
void *buf; /* 调用方缓冲(仅指针,须保持有效至完成) */
|
||||
int len; /* 缓冲容量 */
|
||||
uint32_t deadline; /* 截止时刻(tick)= 登记时刻 + 本次预算 */
|
||||
uint32_t registered; /* 登记时刻(tick;看门狗/日志) */
|
||||
uint32_t deadline; /* 截止时刻(tick)= 登记时刻 + 本次预算(infinite=1 时无意义) */
|
||||
uint32_t registered; /* 登记时刻(tick;tripwire/日志基准) */
|
||||
TaskHandle_t caller; /* 调用方任务(完成通知目标) */
|
||||
} net_pending_t;
|
||||
|
||||
@@ -694,8 +698,8 @@ static void net_reconcile_tick(void) {
|
||||
* (NET_RELISTEN_RETRY_MS)节拍重试 open+listen;2s(NET_RELISTEN_DEADLINE_MS)
|
||||
* 总时限到期放弃留痕
|
||||
* 3. D10:本 fd 有在途挂起 recv 时跳过(L3 保险)
|
||||
* 4. net_pending_tick(L3):按节拍了结挂起 recv(数据/到点/断连)
|
||||
* 5. L3 看门狗:到点后 1s(NET_PENDING_WATCHDOG_MS)未了结 → 强制了结 + DBG_ERROR 留痕
|
||||
* 4. net_pending_tick(L3):按节拍了结挂起 recv(数据/到点[仅显式预算]/断连)
|
||||
* 5. L3 tripwire(N34 阶段 1.5):挂起超 60s 只 DBG_ERROR 留痕一次,不再强制了结/伪造返回值
|
||||
* 6. net_reconcile_tick(Trap 22):每 200ms(NET_RECONCILE_PERIOD_MS)对账①②
|
||||
* 7. 对账以连续 2 次失配去抖 + 1s 冷却,自愈两类失同步
|
||||
* 8. net_listener_pump(N-32 P2):每 100ms 监听器 pump,本层**唯一**重建入口
|
||||
@@ -933,6 +937,10 @@ int net_socket(int domain, int type) {
|
||||
p_sock->send_ready = 1;
|
||||
/* 无可读数据 */
|
||||
p_sock->readable = 0;
|
||||
/* N34 阶段 1.5:阻塞模式默认 = 阻塞(BSD/Python 语义;非阻塞用
|
||||
* net_setblocking(fd, 0) 显式设置)。close 的 memset 会清零本字段,
|
||||
* 新 fd 必须在此显式置 1(同 released_abnormal 的"不得继承"纪律) */
|
||||
p_sock->blocking = 1;
|
||||
/* 新 fd 不得继承上一条连接的释放原因(Trap 23 待办 #3) */
|
||||
p_sock->released_abnormal = 0;
|
||||
/* N-32(P1):新 fd 不得继承"上一条会话已结束"电平(否则新建即 EOF,
|
||||
@@ -1865,16 +1873,17 @@ static int net_send_locked(net_sock_t *p_sock, const void *buf, int len) {
|
||||
* buf - 接收缓冲区指针 void *
|
||||
* len - 缓冲区大小 int > 0
|
||||
* flags - 标志位,NET_MSG_DONTWAIT 表示非阻塞 int
|
||||
* timeout_ms - 本次等待预算(0 = 默认 NET_RECV_TIMEOUT_MS) uint32_t
|
||||
* timeout_ms - 本次等待预算(0 = 无限等待,N34 阶段 1.5 起无默认值)uint32_t
|
||||
* caller - 调用方任务句柄(挂起请求的完成通知目标) TaskHandle_t
|
||||
* 出口参数:buf - 填充接收到的数据
|
||||
* 返回值:实际接收字节数(> 0);0 表示对端关闭;-1 表示出错/非阻塞无数据 int
|
||||
* NET_PENDING_REGISTERED 表示已登记挂起请求(分派据此跳过即时通知)
|
||||
* 限定条件:p_sock 非空;netTask 上下文
|
||||
* 函数说明:1. 数据已在(快速路径)→ 立即读出返回,行为与旧实现一致
|
||||
* 2. 非阻塞模式无数据立即返回 -1(NET_ERR_WOULDBLOCK)
|
||||
* 2. 非阻塞(NET_MSG_DONTWAIT 单次覆盖 **或** socket blocking 属性为 0,
|
||||
* N34 阶段 1.5 模式属性)无数据立即返回 -1(NET_ERR_WOULDBLOCK)
|
||||
* 3. 无数据 + 阻塞等待 → 登记挂起请求(L3 pending 化,E-02 结构演进)
|
||||
* 4. netTask 零占用立即返回;完成条件(数据/到点/断连)由 net_pending_tick 按主循环节拍检查
|
||||
* 4. netTask 零占用立即返回;完成条件(数据/到点[仅显式预算]/断连)由 net_pending_tick 按主循环节拍检查
|
||||
* 5. 并通知 caller;关闭/网络重建/陈旧重同步由对应路径显式了结(取消必通知)
|
||||
* 6. CLOSED 状态语义(排空 + EOF + 自动重监听)逐项保留在 tick 内
|
||||
*/
|
||||
@@ -1888,8 +1897,9 @@ static int net_recv_locked(net_sock_t *p_sock, void *buf, int len, int flags,
|
||||
return NET_ERR_RETURN_1(NET_ERR_INVAL);
|
||||
}
|
||||
|
||||
/* 超时来源:调用方指定(单次预算)优先,否则用默认值(均作为挂起截止) */
|
||||
uint32_t to_ms = (timeout_ms != 0U) ? timeout_ms : (uint32_t)NET_RECV_TIMEOUT_MS;
|
||||
/* 超时来源:只有调用方显式预算(net_recv_to);0 = 无限等待
|
||||
* (N34 阶段 1.5:NET_RECV_TIMEOUT_MS 30s 默认预算已删,API 不强加隐藏值) */
|
||||
uint32_t to_ms = timeout_ms;
|
||||
|
||||
/* 先轮询一次:刷新状态机与可读标志 */
|
||||
net_poll();
|
||||
@@ -1911,8 +1921,9 @@ static int net_recv_locked(net_sock_t *p_sock, void *buf, int len, int flags,
|
||||
return read_len;
|
||||
}
|
||||
|
||||
/* 非阻塞模式(DONTWAIT) */
|
||||
if (flags & NET_MSG_DONTWAIT) {
|
||||
/* 非阻塞模式:NET_MSG_DONTWAIT 单次覆盖,或 socket 的 blocking 属性为 0
|
||||
* (net_setblocking(fd, 0),N34 阶段 1.5 模式挂控制块属性) */
|
||||
if ((flags & NET_MSG_DONTWAIT) || (p_sock->blocking == 0U)) {
|
||||
/* 非阻塞且当前无数据:若连接已断开则视为 EOF(0) 并自动重监听,
|
||||
* 否则返回 WOULDBLOCK(-1) 表示暂时无数据 */
|
||||
/* 连接已断开(CLOSED)**或**本会话已结束(session_dead):按 EOF 处理。
|
||||
@@ -1961,16 +1972,18 @@ static int net_recv_locked(net_sock_t *p_sock, void *buf, int len, int flags,
|
||||
* 2. 清空整个控制块(memset 0),释放该 Socket 资源
|
||||
*/
|
||||
|
||||
/* ===== L3 挂起 recv 请求的实现(一期,2026-09-22) =====
|
||||
* 完成条件按 ①数据 ②到点 ③断连 顺序检查(逐项对齐旧自旋循环语义)
|
||||
/* ===== L3 挂起 recv 请求的实现(一期,2026-09-22;N34 阶段 1.5 语义主流化) =====
|
||||
* 完成条件按 ①数据 ②到点(仅显式预算) ③断连 顺序检查(逐项对齐旧自旋循环语义)
|
||||
* ① 数据通道(ESTABLISHED/UDP_OPEN)内 readable 门控 SPI 读——常见无数据路径零额外 SPI 事务
|
||||
* (INT# 电平触发链保证缓冲有数据时每轮重读 SINT 重位置位)
|
||||
* ② 到点(与状态无关,旧自旋亦在任何状态下检查超时)→ NET_ERR_TIMEDOUT
|
||||
* ② 到点(仅当登记时带显式预算 net_recv_to;无限等待登记无 deadline)→ NET_ERR_TIMEDOUT
|
||||
* ——这是调用方策略的正常兑现,不是伪造
|
||||
* ③ CLOSED:先排空(bit3=1 芯片断连后残数据留缓冲待 MCU 读——Trap 25 排空契约;
|
||||
* 旧注归 Trap 13 已随其复核注降级)→ 有数据按数据完成,否则 EOF(0) + 自动重监听
|
||||
* 其他状态(CONNECTING/LISTENING/…)保持等待状态迁移(CONNECT→ESTABLISHED 是合法路径;
|
||||
* 陈旧 resync 由显式取消处理)——不做状态推断取消,避免误杀正在建链的等待
|
||||
* 看门狗兜底:到点后仍未了结 = 完成逻辑缺位 → 强制了结留痕
|
||||
* tripwire(N34 阶段 1.5 降级):挂起超 60s = "完成逻辑缺位"探针 → 只 [ERR] 留痕一次,
|
||||
* **绝不强制了结、绝不伪造 TIMEDOUT 返回值**(唤醒责任在 ①②③ 与显式 cancel 路径)
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -2012,7 +2025,7 @@ static void net_pending_complete(int sockfd, int result) {
|
||||
* 入口参数:sockfd - Socket 描述符 (0~7) int
|
||||
* buf - 调用方接收缓冲(须保持有效至完成) void *
|
||||
* len - 缓冲容量 int
|
||||
* to_ms - 本次等待预算(截止 = 登记时刻 + to_ms) uint32_t
|
||||
* to_ms - 本次等待预算(0 = 无限等待,N34 阶段 1.5) uint32_t
|
||||
* caller - 调用方任务句柄(完成通知目标) TaskHandle_t
|
||||
* 出口参数:无
|
||||
* 返回值:NET_PENDING_REGISTERED - 登记成功(分派跳过即时通知)
|
||||
@@ -2045,17 +2058,21 @@ static int net_pending_register(int sockfd, void *buf, int len,
|
||||
return NET_ERR_RETURN_1(NET_ERR_BUSY);
|
||||
}
|
||||
|
||||
/* 当前 tick(截止/看门狗基准) */
|
||||
/* 当前 tick(截止/tripwire 基准) */
|
||||
now = HAL_GetTick();
|
||||
/* 标记挂起在位 */
|
||||
p->active = 1U;
|
||||
/* 预算形态:0 = 无限等待(无 deadline,tick ② 跳过;tripwire 以登记时刻计) */
|
||||
p->infinite = (to_ms == 0U) ? 1U : 0U;
|
||||
/* tripwire 留痕标记清零(每轮挂起只留痕一条) */
|
||||
p->tripwire_logged = 0U;
|
||||
/* 调用方接收缓冲(须保持有效至完成) */
|
||||
p->buf = buf;
|
||||
/* 缓冲容量 */
|
||||
p->len = len;
|
||||
/* 截止时刻 = 登记时刻 + 预算 */
|
||||
p->deadline = now + to_ms;
|
||||
/* 登记时刻(看门狗基准) */
|
||||
/* 截止时刻 = 登记时刻 + 预算(无限等待时不启用,置 0 仅为可观测性) */
|
||||
p->deadline = (to_ms == 0U) ? 0U : (now + to_ms);
|
||||
/* 登记时刻(tripwire 基准) */
|
||||
p->registered = now;
|
||||
/* 完成通知目标 */
|
||||
p->caller = caller;
|
||||
@@ -2069,8 +2086,10 @@ static int net_pending_register(int sockfd, void *buf, int len,
|
||||
* 出口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:netTask 上下文
|
||||
* 函数说明:1. 逐 fd 检查完成条件:数据到达 / 到点(deadline)/ 断连
|
||||
* 2. 完成即经 net_pending_complete 通知并清除;看门狗(NET_PENDING_WATCHDOG_MS)超时强制了结
|
||||
* 函数说明:1. 逐 fd 检查完成条件:数据到达 / 到点(仅显式预算)/ 断连
|
||||
* 2. 完成即经 net_pending_complete 通知并清除
|
||||
* 3. tripwire(N34 阶段 1.5):挂起超 NET_RECV_TRIPWIRE_MS 只 [ERR] 留痕
|
||||
* 一次("完成逻辑缺位"探针),绝不强制了结、绝不伪造返回值
|
||||
*/
|
||||
static void net_pending_tick(void) {
|
||||
/* 当前 tick */
|
||||
@@ -2122,8 +2141,10 @@ static void net_pending_tick(void) {
|
||||
}
|
||||
}
|
||||
|
||||
/* ② 到点(tick 差值按有符号比较,抗回绕) */
|
||||
if ((int32_t)(now - p->deadline) >= 0) {
|
||||
/* ② 到点(仅显式预算登记;无限等待登记无 deadline——N34 阶段 1.5:
|
||||
* 超时是调用方策略,net_recv_to 的预算在此兑现,不是伪造。
|
||||
* tick 差值按有符号比较,抗回绕) */
|
||||
if ((p->infinite == 0U) && ((int32_t)(now - p->deadline) >= 0)) {
|
||||
/* 了结:超时(错误码由 net_pending_complete 记入本任务槽位) */
|
||||
net_pending_complete(i, NET_ERR_TIMEDOUT);
|
||||
continue;
|
||||
@@ -2133,7 +2154,9 @@ static void net_pending_tick(void) {
|
||||
* 判据同 net_recv_locked(N-32 P1 不变量 I):`state == CLOSED ||
|
||||
* session_dead`。只判 CLOSED 时,抢先 relisten 后 ①(要求 ESTABLISHED)
|
||||
* 与 ③(要求 CLOSED) 两头落空 → 挂起的 recv 只能干等 ② deadline 超时
|
||||
* (lftpd 阻塞 recv 的"消费者卡死"原型)。 */
|
||||
* (lftpd 阻塞 recv 的"消费者卡死"原型)。N34 阶段 1.5 后该形态更致命:
|
||||
* 无限等待登记没有 ②,漏 ③ = 永久挂死(只剩 tripwire 留痕)——
|
||||
* ③ 与显式 cancel 是无限 recv 的全部唤醒责任方,勿动判据。 */
|
||||
if ((p_sock->state == NET_SOCK_STATE_CLOSED) || (p_sock->session_dead != 0U)) {
|
||||
/* 查询残留字节数 */
|
||||
int rem = ch395f_get_recv_len((uint8_t)i);
|
||||
@@ -2179,13 +2202,23 @@ static void net_pending_tick(void) {
|
||||
|
||||
/* 其他状态:保持等待(见本区块头注释) */
|
||||
|
||||
/* ④ 看门狗:到点 + 裕量仍未了结 = 完成逻辑缺位 → 强制了结并留痕 */
|
||||
if ((int32_t)(now - p->deadline) >= (int32_t)NET_PENDING_WATCHDOG_MS) {
|
||||
/* 强制了结留痕(超截止时刻的毫秒数) */
|
||||
DBG_ERROR("sock%d pending recv stuck %ums past deadline (force settle)",
|
||||
i, (unsigned)((int32_t)now - (int32_t)p->deadline));
|
||||
/* 强制了结:超时(错误码由 net_pending_complete 记入本任务槽位) */
|
||||
net_pending_complete(i, NET_ERR_TIMEDOUT);
|
||||
/* ④ tripwire(N34 阶段 1.5 降级,原"看门狗强制了结"):挂起超 60s =
|
||||
* "完成逻辑缺位"探针 → 只 [ERR] 留痕一次,**绝不强制了结、绝不伪造
|
||||
* TIMEDOUT 返回值**(旧实现到点 +1s 兜底伪造 TIMEDOUT,掩盖完成路径
|
||||
* 缺陷;新语义下无限等待本就无预算,伪造返回值反而违背调用方意图)。
|
||||
* 判据:无限登记按登记时刻计;显式登记按 deadline 计(② 正常不会让它
|
||||
* 越过 60s,能到这里本身就是完成逻辑缺位)。 */
|
||||
if (p->tripwire_logged == 0U) {
|
||||
/* 探针基准时刻:无限 = 登记时刻;显式 = 截止时刻 */
|
||||
uint32_t base = (p->infinite != 0U) ? p->registered : p->deadline;
|
||||
if ((int32_t)(now - base) >= (int32_t)NET_RECV_TRIPWIRE_MS) {
|
||||
/* 留痕(挂起已持续毫秒数,自基准起算) */
|
||||
DBG_ERROR("sock%d recv pending %ums (base=%s): completion source missing",
|
||||
i, (unsigned)((int32_t)now - (int32_t)base),
|
||||
(p->infinite != 0U) ? "infinite" : "deadline");
|
||||
/* 只留痕一次(每轮挂起一条,防日志风暴) */
|
||||
p->tripwire_logged = 1U;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2437,17 +2470,26 @@ int net_send(int sockfd, const void *buf, int len, int flags) {
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:接收数据(线程安全版本,通过消息队列委托给 netTask)
|
||||
* 函数功能:接收数据(线程安全版本,通过消息队列委托给 netTask;
|
||||
* **默认无限阻塞**——N34 阶段 1.5 语义主流化,30s 默认预算已删)
|
||||
* 入口参数:sockfd - Socket 描述符 int >= 0 && < NET_MAX_SOCKETS
|
||||
* buf - 接收缓冲区指针 void *
|
||||
* len - 缓冲区大小 int > 0
|
||||
* flags - 标志位,0=阻塞,NET_MSG_DONTWAIT=非阻塞 int
|
||||
* flags - 标志位,0=按 socket 阻塞属性等待,NET_MSG_DONTWAIT=单次非阻塞 int
|
||||
* 出口参数:无
|
||||
* 返回值:实际接收字节数 int >= 0 || 0 || -1
|
||||
* 0 - 对端关闭连接 int
|
||||
* -1 - 接收失败(参数无效、队列满等) int
|
||||
* -1 - 接收失败(参数无效、非阻塞无数据=WOULDBLOCK、
|
||||
* close/网络重建/陈旧重同步取消=CONNRESET 等,errno 可查) int
|
||||
* 限定条件:Socket 处于 ESTABLISHED 状态;net_init() + MX_FREERTOS_Init() 已执行;非 netTask 上下文
|
||||
* 函数说明:将接收请求放入消息队列,阻塞等待 netTask 处理完毕并返回结果。netTask 内请使用 net_recv_sock()
|
||||
* 函数说明:1. 将接收请求放入消息队列,等待 netTask 处理完毕并返回结果。netTask 内请使用 net_recv_sock()
|
||||
* 2. 无数据 + 阻塞(blocking 属性为默认值 1 且未置 DONTWAIT)→ 登记挂起
|
||||
* **无限等待**;唤醒源 = 数据 / EOF / close / 网络重建(CONNRESET);
|
||||
* 会话死亡由对账① ~400ms 判死兜底,不存在"对端消失后永久挂死"窗口
|
||||
* 3. ⚠ 文档契约(嵌入式特有):IWDG required_mask 内任务禁止无限阻塞
|
||||
* recv——必须 net_select 超时或 net_recv_to 显式预算(超时归使用者
|
||||
* 对受监控任务是义务不是选项),否则"对端连上不发数据"= IWDG 复位
|
||||
* 4. 超时首选 net_select(...);net_recv_to(...) 为显式预算便捷入口
|
||||
*/
|
||||
int net_recv(int sockfd, void *buf, int len, int flags) {
|
||||
/* 一次性消息结构体(队列按值存结构副本) */
|
||||
@@ -2471,9 +2513,9 @@ int net_recv(int sockfd, void *buf, int len, int flags) {
|
||||
msg.buf = buf;
|
||||
/* 缓冲容量 */
|
||||
msg.len = len;
|
||||
/* 标志:0=阻塞,NET_MSG_DONTWAIT=非阻塞 */
|
||||
/* 标志:0=按 socket 阻塞属性,NET_MSG_DONTWAIT=单次非阻塞 */
|
||||
msg.flags = flags;
|
||||
/* 0 = 默认超时预算(netTask 侧用 NET_RECV_TIMEOUT_MS) */
|
||||
/* 0 = 无限等待(N34 阶段 1.5:无默认预算;显式预算走 net_recv_to) */
|
||||
msg.timeout_ms = 0U;
|
||||
/* 调用方任务句柄(netTask 凭它回传回执) */
|
||||
msg.caller = xTaskGetCurrentTaskHandle();
|
||||
@@ -2499,7 +2541,28 @@ int net_recv(int sockfd, void *buf, int len, int flags) {
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:接收数据(带调用方指定超时的阻塞版,用于分片有界读)
|
||||
* 函数功能:接收数据(非阻塞具名入口——N34 阶段 1.5 把 NET_MSG_DONTWAIT 路径转正,
|
||||
* 对齐 BSD recv(MSG_DONTWAIT) / Python recv 非阻塞模式的"名如其义")
|
||||
* 入口参数:sockfd - Socket 描述符 int >= 0 && < NET_MAX_SOCKETS
|
||||
* buf - 接收缓冲区指针 void *
|
||||
* len - 缓冲区大小 int > 0
|
||||
* 出口参数:buf - 填充接收到的数据
|
||||
* 返回值:实际接收字节数(> 0);0 = 对端关闭(net 层已自动重监听); int >= 0 || -1
|
||||
* -1 = 错误(无数据 = NET_ERR_WOULDBLOCK,其余 errno 可查) int
|
||||
* 限定条件:非 netTask 上下文;net 层已初始化
|
||||
* 函数说明:1. 等价 net_recv(sockfd, buf, len, NET_MSG_DONTWAIT):单次强制非阻塞,
|
||||
* 不改变 socket 的 blocking 属性(模式用 net_setblocking)
|
||||
* 2. 受 IWDG 监控 / 有周期职责的任务(netTask 除外)的推荐默认入口;
|
||||
* 需要"等但不无限等"用 net_select / net_recv_to
|
||||
*/
|
||||
int net_recv_nb(int sockfd, void *buf, int len) {
|
||||
return net_recv(sockfd, buf, len, NET_MSG_DONTWAIT);
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:接收数据(带调用方指定超时的阻塞版,用于分片有界读;
|
||||
* N34 阶段 1.5 起为**显式预算**便捷入口,等价 Python settimeout,
|
||||
* 不再有隐藏默认值)
|
||||
* 入口参数:sockfd - Socket 描述符 int >= 0 && < NET_MAX_SOCKETS
|
||||
* buf / len - 接收缓冲与容量 void * / int > 0
|
||||
* timeout_ms - 单次占用 netTask 的最长等待(毫秒,>0) uint32_t
|
||||
@@ -2507,10 +2570,11 @@ int net_recv(int sockfd, void *buf, int len, int flags) {
|
||||
* 返回值:>0 字节数;0 = 对端关闭(EOF);-1 = 真错误(errno 可查);**-2 = 超时无数据**
|
||||
* 限定条件:非 netTask 上下文;net 层已初始化
|
||||
* 函数说明:1. E-02 修复配套接口:lftpd 等"等对端下一行/下一块数据"的调用方
|
||||
* 若用默认 30s 阻塞 recv,会把 netTask(全部 socket 操作的唯一执行者)长期占住
|
||||
* 若无限阻塞 recv,会把 netTask(全部 socket 操作的唯一执行者)长期占住
|
||||
* 2. 导致其余任务的网络操作整体冻结(M3 实测一次空闲 FTP 登录冻结远端显示 17.7s)
|
||||
* 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) {
|
||||
/* 一次性消息结构体:队列按值存结构副本(仅指针、不拷数据),入队后由 netTask 取出处理 */
|
||||
@@ -2528,8 +2592,9 @@ int net_recv_to(int sockfd, void *buf, int len, uint32_t timeout_ms) {
|
||||
return NET_ERR_RETURN_1(NET_ERR);
|
||||
}
|
||||
|
||||
/* ---- 3. 分片长度检查:timeout_ms = 0 在 netTask 侧意味着"改用默认
|
||||
* NET_RECV_TIMEOUT_MS(30 秒)",恰是本接口要规避的长阻塞,直接拒绝 ---- */
|
||||
/* ---- 3. 分片长度检查:timeout_ms = 0 在 netTask 侧意味着"**无限等待**"
|
||||
* (N34 阶段 1.5 删除 30s 默认预算),恰与本接口"有界等待"的存在
|
||||
* 理由相反,直接拒绝 ---- */
|
||||
if (timeout_ms == 0U) {
|
||||
/* 分片长度必须 > 0 */
|
||||
return NET_ERR_RETURN_1(NET_ERR_INVAL);
|
||||
@@ -2780,14 +2845,14 @@ int net_sendto(int sockfd, const void *buf, int len, int flags,
|
||||
* 限定条件:Socket 已创建且为 UDP 类型(SOCK_DGRAM);net_init() 已调用;有数据可用
|
||||
* 函数说明:1. 轮询检查芯片接收缓冲(net_poll + get_recv_len),有数据即返回
|
||||
* 2. UDP 数据报前 8 字节为报文头(源端口+源 IP),据此填充 src_addr
|
||||
* 3. 无数据时:DONTWAIT 立即返回 -1(WOULDBLOCK);默认等待 NET_RECV_TIMEOUT_MS 后返回 TIMEDOUT
|
||||
* 3. 无数据时(N34 阶段 1.5,与 net_recv 同构):DONTWAIT / 非阻塞属性
|
||||
* 立即返回 -1(WOULDBLOCK);阻塞模式**无限等待**(30s 默认预算已删;
|
||||
* 本接口为轮询自旋实现且无生产调用者,受 IWDG 监控的任务勿用无限形态)
|
||||
*/
|
||||
int net_recvfrom(int sockfd, void *buf, int len, int flags,
|
||||
struct net_sockaddr *src_addr, int *addrlen) {
|
||||
/* Socket 控制块指针 */
|
||||
net_sock_t *p_sock;
|
||||
/* 起始时刻(超时判定基准,tick) */
|
||||
uint32_t tick_start;
|
||||
|
||||
/* 参数检查 */
|
||||
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS) {
|
||||
@@ -2807,10 +2872,7 @@ int net_recvfrom(int sockfd, void *buf, int len, int flags,
|
||||
return NET_ERR_RETURN_1(NET_ERR_INVAL);
|
||||
}
|
||||
|
||||
/* 记录起始时刻(超时基准) */
|
||||
tick_start = HAL_GetTick();
|
||||
|
||||
/* 轮询直到:有数据 / 非阻塞 / 超时 */
|
||||
/* 轮询直到:有数据 / 非阻塞(N34 阶段 1.5:阻塞 = 无限等待,无默认预算) */
|
||||
while (1) {
|
||||
/* 轮询一次:更新状态机与可读标志 */
|
||||
net_poll();
|
||||
@@ -2862,18 +2924,16 @@ int net_recvfrom(int sockfd, void *buf, int len, int flags,
|
||||
}
|
||||
}
|
||||
|
||||
/* 非阻塞模式:无数据立即返回 */
|
||||
if (flags & NET_MSG_DONTWAIT) {
|
||||
/* 非阻塞模式:NET_MSG_DONTWAIT 单次覆盖,或 socket blocking 属性为 0
|
||||
* (N34 阶段 1.5,与 net_recv_locked 同判据):无数据立即返回 */
|
||||
if ((flags & NET_MSG_DONTWAIT) || (p_sock->blocking == 0U)) {
|
||||
/* 置 WOULDBLOCK */
|
||||
return NET_ERR_RETURN_1(NET_ERR_WOULDBLOCK);
|
||||
}
|
||||
|
||||
/* 超时检查 */
|
||||
if (NET_RECV_TIMEOUT_MS > 0 &&
|
||||
(HAL_GetTick() - tick_start) >= NET_RECV_TIMEOUT_MS) {
|
||||
/* 置 TIMEDOUT */
|
||||
return NET_ERR_RETURN_1(NET_ERR_TIMEDOUT);
|
||||
}
|
||||
/* 阻塞模式:无数据继续轮询等待(N34 阶段 1.5:无限等待,无默认预算——
|
||||
* 旧 NET_RECV_TIMEOUT_MS 30s 兜底已删;唤醒源 = 数据,与 net_recv 的
|
||||
* 差异是本接口为自旋实现,勿在受 IWDG 监控任务中使用无限形态) */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2916,7 +2976,8 @@ int net_recv_sock(net_sock_t *p_sock, void *buf, int len, int flags) {
|
||||
return NET_ERR_RETURN_1(NET_ERR_BADF);
|
||||
}
|
||||
/* 直接委托锁定实现(netTask 上下文,无队列往返);
|
||||
* 0 = 默认 NET_RECV_TIMEOUT_MS;caller = netTask 自身(自调用) */
|
||||
* timeout_ms = 0(无限等待语义,但 netTask 自调用会被挂起登记拒绝 →
|
||||
* 本接口实际仅 DONTWAIT / 非阻塞属性可用);caller = netTask 自身 */
|
||||
return net_recv_locked(p_sock, buf, len, flags, 0U,
|
||||
xTaskGetCurrentTaskHandle());
|
||||
}
|
||||
@@ -2975,6 +3036,46 @@ int net_set_event_cb(int sockfd, net_event_cb_t cb, void *arg) {
|
||||
/* 注册成功 */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:设置 Socket 阻塞/非阻塞**模式属性**(N34 阶段 1.5,模式挂控制块属性,
|
||||
* 对齐 BSD O_NONBLOCK / Python setblocking)
|
||||
* 入口参数:sockfd - Socket 描述符 int >= 0 && < NET_MAX_SOCKETS
|
||||
* on - 1 = 阻塞(net_socket 默认);0 = 非阻塞 int
|
||||
* 出口参数:无
|
||||
* 返回值:0 - 设置成功 int
|
||||
* -1 - 失败(描述符越界 / 未占用) int
|
||||
* 限定条件:socket 已创建(in_use == 1);任意上下文(同 net_set_event_cb 先例)
|
||||
* 函数说明:1. 属性对 net_recv / net_recv_sock 生效:非阻塞下无数据立即
|
||||
* NET_ERR_WOULDBLOCK;EOF 判定语义不变(CLOSED/session_dead 无数据
|
||||
* 仍返回 0 并触发自动重监听)
|
||||
* 2. 直写控制块单字节(ARM 原子写,先例 net_set_event_cb):与 netTask
|
||||
* 竞态的最坏结果 = 一次在途 recv 用旧模式,无撕裂、无需过消息队列
|
||||
* 3. 与超时预算正交:模式是粘滞属性;单次预算用 net_recv_to / net_select
|
||||
*/
|
||||
int net_setblocking(int sockfd, int on) {
|
||||
/* Socket 控制块指针 */
|
||||
net_sock_t *p_sock;
|
||||
|
||||
/* 参数检查:描述符范围 */
|
||||
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS) {
|
||||
/* 描述符越界 → BADF */
|
||||
return NET_ERR_RETURN_1(NET_ERR_BADF);
|
||||
}
|
||||
|
||||
/* 取控制块 */
|
||||
p_sock = &s_net_socks[sockfd];
|
||||
/* 控制块未在用 → BADF */
|
||||
if (!p_sock->in_use) {
|
||||
return NET_ERR_RETURN_1(NET_ERR_BADF);
|
||||
}
|
||||
|
||||
/* 写模式属性:非 0 = 阻塞,0 = 非阻塞(单字节原子写) */
|
||||
p_sock->blocking = (on != 0) ? 1U : 0U;
|
||||
|
||||
/* 设置成功 */
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* 函数功能:获取 **本任务** 最后一次 net_*() 调用失败时的精确错误码
|
||||
* 入口参数:无
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -47,7 +47,9 @@ extern "C" {
|
||||
/*
|
||||
* 接收标志定义
|
||||
*/
|
||||
#define NET_MSG_DONTWAIT 0x01 /* 非阻塞模式 */
|
||||
#define NET_MSG_DONTWAIT 0x01 /* 非阻塞模式(单次覆盖 socket 的 blocking 属性;
|
||||
* 具名入口见 net_recv_nb() / net_setblocking(),
|
||||
* N34 阶段 1.5) */
|
||||
|
||||
/*
|
||||
* 网络字节序转换宏
|
||||
@@ -171,6 +173,15 @@ typedef struct {
|
||||
/* 单连接模式自动重监听:连接断开后由 net 层自动回到 LISTEN(BSD 风格监听常驻) */
|
||||
uint8_t auto_relisten;
|
||||
|
||||
/* N34 阶段 1.5(2026-10-13):阻塞/非阻塞**模式属性**(对齐 BSD O_NONBLOCK /
|
||||
* Python setblocking 的"模式归使用者"模型)。
|
||||
* 1 = 阻塞(net_socket() 默认);0 = 非阻塞(net_setblocking(fd, 0))。
|
||||
* net_recv() 无 NET_MSG_DONTWAIT 时按本属性决定"无数据 → 登记挂起等待"
|
||||
* 还是"立即 WOULDBLOCK"。单字节写(ARM 原子),写入先例同 event_cb
|
||||
* (net_setblocking 直写控制块,最坏一次 recv 用旧模式,无撕裂风险)。
|
||||
* 注意与"超时预算"正交:模式是属性(粘滞),预算是单次参数(net_recv_to)。 */
|
||||
uint8_t blocking;
|
||||
|
||||
/* 释放来源标记(Trap 23 待办 #3):1 = 异常释放(芯片 TIMEOUT,对端 RST/掉链),
|
||||
* 0 = 正常释放(芯片 DISCONNECT,对端 FIN)或新连接。L3 挂起 recv 的了结断连
|
||||
* 分支据此决定回 EOF(0) 还是 NET_ERR_CONNRESET:异常结束上报应用(lftpd STOR)
|
||||
|
||||
@@ -545,8 +545,10 @@ void ui_net_sink_poll(void) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* 有客户端时非阻塞收帧(I-1 门控 + I-2 兜底):
|
||||
* 无数据可读 → 多数轮次直接返回,省掉一次 net_recv 往返(该往返内部
|
||||
/* 有客户端时非阻塞收帧(I-1 门控 + I-2 兜底;N34 阶段 1.5:DONTWAIT 转正
|
||||
* 为具名 API net_recv_nb——uiTask 在 IWDG required_mask 内且有 10ms UI 推流
|
||||
* 周期职责,禁止无限阻塞 recv,非阻塞轮询是义务):
|
||||
* 无数据可读 → 多数轮次直接返回,省掉一次 recv 往返(该往返内部
|
||||
* osMessageQueuePut(osWaitForever) + xTaskNotifyWait(portMAX_DELAY),无上界);
|
||||
* 但每 RM_LIVENESS_DIV 轮无条件收一次,防 RECV_OK 中断丢失把数据永久藏住 */
|
||||
if (net_can_read(g_client) == 0) {
|
||||
@@ -556,7 +558,7 @@ void ui_net_sink_poll(void) {
|
||||
} else {
|
||||
s_liveness_ctr = 0U;
|
||||
}
|
||||
n = net_recv(g_client, g_rx, (int)sizeof(g_rx), NET_MSG_DONTWAIT);
|
||||
n = net_recv_nb(g_client, g_rx, (int)sizeof(g_rx));
|
||||
if (n > 0) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
rm_parser_feed((uint8_t)g_rx[i]);
|
||||
|
||||
@@ -45,8 +45,10 @@ int ui_net_sink_start(uint16_t port);
|
||||
* 返回值:无
|
||||
* 限定条件:ui_net_sink_start() 已成功调用
|
||||
* 函数说明:1. 无客户端时先门控(net_listener_armed,纯 RAM 读)再尝试接受连接
|
||||
* 2. 有客户端时先门控(net_can_read)再用 DONTWAIT 收帧并解析
|
||||
* 2. 有客户端时先门控(net_can_read)再用 net_recv_nb 非阻塞收帧并解析
|
||||
* (KEEPLIVE/INIT/KEY/LCDMEM);门控判为无数据时仍每 32 轮兜底收一次
|
||||
* (N34 阶段 1.5:DONTWAIT 转正为具名 API;uiTask 受 IWDG 监控,
|
||||
* 非阻塞是契约义务——见 net_socket.h net_recv 文档契约)
|
||||
* 3. 对端关闭或异常时复位客户端,等待下次重监听
|
||||
* ⚠ **不是"零成本非阻塞"**:门控只消除无谓往返;真有活时仍是
|
||||
* "入队 + 等 netTask 回执"(osMessageQueuePut(osWaitForever) +
|
||||
|
||||
Reference in New Issue
Block a user