#define DBG_TAG "[LFT_INET]" #include "private/lftpd_inet.h" #include "dbg_log.h" #include "net_socket.h" #include /* * E-02 头阻塞修复 + L3 pending 化(2026-09-22,M3 台架实证): * netTask 是全部 socket 操作的唯一执行者;lftpd 原先用默认 30s 的**阻塞** * net_recv 等待对端下一行/下一块数据 → 只要客户端连上却不发完整行,netTask * 就被长期占用,其余任务(含远端显示)的网络操作整体冻结(实测一次空闲 FTP * 登录让 LCDMEM 整帧等待 17.7s)。 * 2026-09-22 先落地分片有界读(5ms 片;实测 100ms 片下受压帧仍达 ~750ms, * 收到 5ms 后降到 ~10ms 量级,见 m3_data 修复后数据),同日 L3 pending 化 * 后改为"挂起请求":每次等待 = 一次挂起登记(netTask 零占用,完成按主循环 * 节拍检查),调用方单次超时 = 剩余总预算(总等待 ≤ LFTPD_TOTAL_BUDGET_MS, * **总超时语义与旧实现一致**),旧分片协议退役。 */ #define LFTPD_TOTAL_BUDGET_MS 30000U int lftpd_inet_listen(int port) { int retry; for (retry = 0; retry < 5; retry++) { 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)); } } DBG_ERROR("listen failed after 5 retries (total 10s wait)"); return -1; } int lftpd_inet_get_socket_port(int socket) { struct net_sockaddr_in addr; int addrlen = sizeof(addr); if (net_getsockname(socket, (struct net_sockaddr *)&addr, &addrlen) < 0) { DBG_ERROR("error getting socket port number"); return -1; } return (int)net_ntohs(addr.sin_port); } int lftpd_inet_accept(int socket) { /* 网络重建(net_init)后旧 Socket 已失效:检测到代次变化即返回 -1, * 由 lftpd_start 外层循环重建监听(网络未就绪时其 listen 会重试) */ uint32_t gen = net_get_generation(); while (1) { int s = net_accept(socket, NULL, NULL); if (s >= 0) { return s; } if (net_get_generation() != gen) { DBG_ERROR("network re-init: abort accept, relisten"); return -1; } vTaskDelay(pdMS_TO_TICKS(100)); } } int lftpd_inet_accept_timeout(int socket, int timeout_ms) { int elapsed = 0; while (elapsed < timeout_ms) { int s = net_accept(socket, NULL, NULL); if (s >= 0) { return s; } /* 数据连接已被对端关闭时提前退出,不等待超时 */ net_sock_t *p_sock = net_get_sock(socket); if (p_sock == NULL || p_sock->state != NET_SOCK_STATE_LISTENING) { DBG_ERROR("data socket closed, accept aborted"); return -1; } vTaskDelay(pdMS_TO_TICKS(100)); elapsed += 100; } DBG_ERROR("accept timeout (%dms)", timeout_ms); return -1; } int lftpd_inet_read_line(int socket, char *buffer, size_t buffer_len) { TickType_t t0 = xTaskGetTickCount(); memset(buffer, 0, buffer_len); int total_read_len = 0; while (total_read_len < (int)buffer_len) { /* 单次预算 = 剩余总预算(L3 pending:等待期 netTask 零占用,调用方 * 在自己上下文睡任务通知,旧片间 vTaskDelay(1) 让出退役) */ TickType_t elapsed = xTaskGetTickCount() - t0; uint32_t rem = (elapsed >= (TickType_t)pdMS_TO_TICKS(LFTPD_TOTAL_BUDGET_MS)) ? 0U : (uint32_t)((TickType_t)LFTPD_TOTAL_BUDGET_MS - elapsed); if (rem == 0U) { return -1; /* 总预算耗尽(语义同旧 30s 超时) */ } int read_len = net_recv_to(socket, buffer + total_read_len, (int)(buffer_len - total_read_len - 1), rem); if (read_len == 0) { return -1; /* 对端关闭 */ } if (read_len == -2) { continue; /* 本次预算等满无数据 → 下轮 rem=0 退出 */ } if (read_len < 0) { return read_len; /* 真错误 */ } total_read_len += read_len; char *p = strstr(buffer, "\r\n"); if (p != NULL) { *p = '\0'; return 0; } } return -1; } int lftpd_inet_write_string(int socket, const char *message) { const char *p = message; int length = (int)strlen(message); while (length > 0) { int write_len = net_send(socket, p, length, 0); /* write_len == 0:发送超时且零进展(对端未消费数据);不判为致命则 * length 永不减少,将在此空转(任务死锁)。返回错误交会话层拆除 * 连接(lftpd.c handle_control_channel 的 cleanup 路径)。 */ if (write_len <= 0) { DBG_ERROR("write error, ret=%d", write_len); return -1; } p += write_len; length -= write_len; } return 0; } int lftpd_inet_write(int socket, const void *buf, int len) { const char *p = (const char *)buf; int remaining = len; while (remaining > 0) { int n = net_send(socket, p, remaining, 0); /* n == 0:发送超时且零进展(对端停滞/断链),判为致命:否则 remaining * 永不减少 → 死循环。调用方(lftpd.c send_file)收到 < 0 后终止传输 * 并向上报 450 错误,会话保持可用。 */ if (n <= 0) { DBG_ERROR("write error, ret=%d", n); return -1; } p += n; remaining -= n; } return len; } int lftpd_inet_read(int socket, void *buf, int len) { /* L3 pending 读:总预算 30s 不变,等待期 netTask 零占用 * (空闲数据连接不再冻结全网;旧分片协议随 L3 pending 化退役) */ TickType_t t0 = xTaskGetTickCount(); for (;;) { TickType_t elapsed = xTaskGetTickCount() - t0; uint32_t rem = (elapsed >= (TickType_t)pdMS_TO_TICKS(LFTPD_TOTAL_BUDGET_MS)) ? 0U : (uint32_t)((TickType_t)LFTPD_TOTAL_BUDGET_MS - elapsed); if (rem == 0U) { return -1; /* 总预算耗尽(语义同旧 30s 超时) */ } int n = net_recv_to(socket, buf, len, rem); if (n >= 0) { return n; /* >0 数据 / 0 EOF */ } if (n == -1) { return -1; /* 真错误 */ } /* n == -2:本次预算等满无数据 → 下轮 rem=0 退出 */ } } int lftpd_inet_close(int socket) { return net_close(socket); }