Files
STM32F4-Base/Drivers/BSP/NET/lftpd/lftpd_inet.c
T

250 lines
8.5 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#define DBG_TAG "[LFT_INET]"
#include "private/lftpd_inet.h"
#include "dbg_log.h"
#include "net_socket.h"
#include <string.h>
/*
* 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
/*
* 看门狗心跳钩子弱默认实现:空(未接应用层的构建——如模拟器——不喂心跳,
* 看门狗为 MCU 构建专属概念)。MCU 构建下 App/util/task_watchdog.c 提供
* 同名强符号(路由到 task_wd_beat(TASK_WD_FTP)),链接器取强符号
* (与 wd_platform_* 钩子同机制)。
*/
#if defined(__CC_ARM)
__weak
#endif
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));
}
}
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;
}
lftpd_task_wd_beat(); /* 看门狗:自旋轮次喂心跳(无限等待,可能长时间无客户端) */
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;
}
lftpd_task_wd_beat(); /* 看门狗:每轮喂心跳(数据连接等待最长 5s) */
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 超时) */
}
lftpd_task_wd_beat(); /* 看门狗:每片前喂心跳(长等待无数据期保持节奏) */
if (rem > LFTPD_WAIT_SLICE_MS) {
rem = LFTPD_WAIT_SLICE_MS; /* 分片:单次 30s 挂起拆为 ≤200ms 片 */
}
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 超时) */
}
lftpd_task_wd_beat(); /* 看门狗:每片前喂心跳(长等待无数据期保持节奏) */
if (rem > LFTPD_WAIT_SLICE_MS) {
rem = LFTPD_WAIT_SLICE_MS; /* 分片:单次 30s 挂起拆为 ≤200ms 片 */
}
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);
}