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

226 lines
7.4 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 头阻塞修复(2026-09-22 M3 台架实证):
* netTask 是全部 socket 操作的唯一执行者;lftpd 原先用默认 30s 的**阻塞**
* net_recv 等待对端下一行/下一块数据 → 只要客户端连上却不发完整行,netTask
* 就被长期占用,其余任务(含远端显示)的网络操作整体冻结(实测一次空闲 FTP
* 登录让 LCDMEM 整帧等待 17.7s)。
* 修法:把等待切成 ≤LFTPD_NET_SLICE_MS 的片(每片 netTask 占用有界),
* 调用方累计总预算 LFTPD_TOTAL_BUDGET_MS —— **总超时语义与旧实现一致**,
* 但任何时刻都不再有"秒级冻结"。单片超时由 net_recv_to 返回 -2 区分(无 errno 竞态)。
* 分片长度即"其他任务最坏等待":实测 100ms 片下受压帧仍达 ~750ms(多片排队),
* 收到 5ms 后受压帧降到 ~10ms 量级(见 m3_data 修复后数据)。
*/
#define LFTPD_NET_SLICE_MS 5U
#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) {
/* 分片有界读(E-02):单片 netTask 占用 ≤ LFTPD_NET_SLICE_MS */
int read_len = net_recv_to(socket,
buffer + total_read_len,
(int)(buffer_len - total_read_len - 1),
LFTPD_NET_SLICE_MS);
if (read_len == 0) {
return -1; /* 对端关闭 */
}
if (read_len == -2) {
/* 本片无数据:累计总预算,未超则继续等。
* **必须显式让出 CPU**:若某片因 net_poll 自身耗时而在首轮就返回 -2,
* 本任务(Normal)立即重排队会形成忙循环 → 饿死 BelowNormal 的 uiTask
* → 远端显示请求数秒无人拾取(2026-09-22 M3 实测 2.4~3.4s 周期尖刺)。
* vTaskDelay(1) 保证低优先级任务每毫秒都有运行机会。 */
if ((xTaskGetTickCount() - t0) >= pdMS_TO_TICKS(LFTPD_TOTAL_BUDGET_MS)) {
return -1; /* 总预算耗尽(语义同旧 30s 超时) */
}
vTaskDelay(pdMS_TO_TICKS(1));
continue;
}
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)
{
/* 分片有界读(E-02):总预算 30s 不变,但每片 netTask 占用 ≤ LFTPD_NET_SLICE_MS,
* 避免空闲数据连接(客户端开了 PASV 却不发数据)冻结全网 */
TickType_t t0 = xTaskGetTickCount();
for (;;) {
int n = net_recv_to(socket, buf, len, LFTPD_NET_SLICE_MS);
if (n >= 0) {
return n; /* >0 数据 / 0 EOF */
}
if (n == -1) {
return -1; /* 真错误 */
}
if ((xTaskGetTickCount() - t0) >= pdMS_TO_TICKS(LFTPD_TOTAL_BUDGET_MS)) {
return -1; /* 总预算耗尽(语义同旧 30s 超时) */
}
vTaskDelay(pdMS_TO_TICKS(1)); /* 让出 CPU:防空闲时忙循环饿死低优先级任务 */
}
}
int lftpd_inet_close(int socket)
{
return net_close(socket);
}