From f50b9c9e664c74dbc97ef516aa1462ee25ecde98 Mon Sep 17 00:00:00 2001 From: wandering1 <1624155937@qq.com> Date: Tue, 21 Jul 2026 01:22:09 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=BD=91=E7=BB=9C=E7=9A=84?= =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=EF=BC=8C=E5=BC=82=E6=AD=A5=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App/task/net_task.c | 59 +++++++- Drivers/BSP/NET/net_socket.c | 208 ++++++++++++++++++++-------- Drivers/BSP/NET/net_socket.h | 11 ++ Drivers/BSP/NET/net_types.h | 3 + MDK-ARM/build_log.txt | 68 ++++----- Src/main.c | 46 ------ docs/NET_Socket_FTP_Support_Plan.md | 80 +++++++++++ 7 files changed, 337 insertions(+), 138 deletions(-) create mode 100644 docs/NET_Socket_FTP_Support_Plan.md diff --git a/App/task/net_task.c b/App/task/net_task.c index 5c4cdab..aa2c1f5 100644 --- a/App/task/net_task.c +++ b/App/task/net_task.c @@ -1,6 +1,6 @@ /* * 模块名称:Network Application Task - * 模块功能:CH395F 协议栈轮询 + TCP/UDP 业务处理 + * 模块功能:CH395F 协议栈初始化 + 轮询 + TCP/UDP 业务处理 * 与 net_socket.h 配合,在 FreeRTOS 独立任务中运行 * 适用平台:STM32F407ZGTx + CH395F * 作者:王建锋 @@ -8,12 +8,15 @@ * 修改记录: * v1.0 2026-07-19 王建锋 创建初始版本 * v1.1 2026-07-19 王建锋 migrate from freertos.c StartNetTask + * v1.2 2026-07-21 王建锋 网络初始化移入本任务(net_init + TCP 监听) */ +#include #include "net_task.h" #include "cmsis_os.h" #include "net_socket.h" #include "net_select.h" +#include "ch395f.h" #include "ch395f_test.h" #define DBG_TAG "[NET_TASK]" @@ -38,16 +41,66 @@ int net_task_init(void) * 函数功能:网络任务主函数 * 入口参数:arg - FreeRTOS 传入参数(未使用) * 返 回 值:无 - * 函数说明:FreeRTOS 任务函数,启动后先等待 2 秒再进入轮询循环 - * 依次处理 net_poll、TCP echo、UDP/Select 测试 + * 函数说明:启动后先调用 net_init 初始化 CH395F 协议栈,创建 TCP 监听, + * 再进入轮询循环处理网络事件 */ void net_task_func(void const *arg) { (void)arg; osDelay(2000); + + /* ==================== CH395F 协议栈初始化 ==================== */ + DBG_INFO("=== Network Init ==="); + + if (net_init("192.168.1.100", "255.255.255.0", "192.168.1.1") == 0) { + DBG_INFO("net init: OK"); + + uint8_t mac[6]; + ch395f_get_mac_addr(mac); + DBG_INFO("MAC: %02X:%02X:%02X:%02X:%02X:%02X", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + ch395f_set_phy(CH395F_PHY_100M_FULL); + HAL_Delay(3000); + uint8_t phy = ch395f_get_phy_status(); + DBG_INFO("PHY: %s", + (phy == CH395F_PHY_100M_FULL) ? "100M FULL" : + (phy == CH395F_PHY_100M_HALF) ? "100M HALF" : + (phy == CH395F_PHY_10M_FULL) ? "10M FULL" : + (phy == CH395F_PHY_10M_HALF) ? "10M HALF" : + (phy == CH395F_PHY_DISCONN) ? "DISCONNECT" : "UNKNOWN"); + } else { + DBG_ERROR("net init: FAIL"); + } + + /* ==================== TCP Server Echo 测试 ==================== */ + { + int listen_sock = net_socket(NET_AF_INET, NET_SOCK_STREAM, 0); + DBG_INFO("TCP listen socket: %d", listen_sock); + + struct net_sockaddr_in serv_addr; + memset(&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = NET_AF_INET; + serv_addr.sin_port = net_htons(8080); + serv_addr.sin_addr.s_addr = NET_INADDR_ANY; + + if (net_bind(listen_sock, (struct net_sockaddr *)&serv_addr, sizeof(serv_addr)) == 0) { + DBG_INFO("TCP bind: OK (port 8080)"); + } else { + DBG_ERROR("TCP bind: FAIL"); + } + + if (net_listen_locked(listen_sock, NET_TCP_SERVER_MAX_CLIENTS) == 0) { + DBG_INFO("TCP listen: OK (max_clients=%d)", NET_TCP_SERVER_MAX_CLIENTS); + } else { + DBG_ERROR("TCP listen: FAIL"); + } + } + DBG_INFO("netTask: started, processing messages"); + /* ==================== 主轮询循环 ==================== */ for (;;) { net_poll(); net_process_messages(); diff --git a/Drivers/BSP/NET/net_socket.c b/Drivers/BSP/NET/net_socket.c index d48c8d7..b6cc4fe 100644 --- a/Drivers/BSP/NET/net_socket.c +++ b/Drivers/BSP/NET/net_socket.c @@ -66,6 +66,7 @@ static void parse_ip_string(const char *str, uint8_t *ip_arr); static int net_send_locked(net_sock_t *p_sock, const void *buf, int len); static int net_recv_locked(net_sock_t *p_sock, void *buf, int len, int flags); static int net_close_locked(net_sock_t *p_sock); +static int net_accept_locked(int sockfd, struct net_sockaddr *addr, int *addrlen); /* * 函数功能:网络子系统初始化(配置 CH395F + 启用多连接模式) @@ -333,6 +334,17 @@ void net_process_messages(void) { msg.addrlen); break; } + case NET_MSG_LISTEN: { + msg.result = net_listen_locked(msg.sockfd, msg.flags); + break; + } + case NET_MSG_ACCEPT: { + struct net_sockaddr *p_addr = (struct net_sockaddr *)msg.buf; + int addrlen = msg.len; + int *p_addrlen = (p_addr != NULL && addrlen > 0) ? &addrlen : NULL; + msg.result = net_accept_locked(msg.sockfd, p_addr, p_addrlen); + break; + } default: msg.result = -1; break; @@ -437,10 +449,11 @@ int net_bind(int sockfd, const struct net_sockaddr *addr, int addrlen) { ch395f_set_des_port((uint8_t)sockfd, 0); status = ch395f_open_socket((uint8_t)sockfd); - if (status != CH395F_ERR_SUCCESS) { - s_net_errno = NET_ERR_BUSY; - return -1; - } + if (status != CH395F_ERR_SUCCESS) { + DBG_ERROR("sock%d open_socket FAIL: status=0x%02X (expected 0x00)", sockfd, status); + s_net_errno = NET_ERR_BUSY; + return -1; + } p_sock->state = NET_SOCK_STATE_UDP_OPEN; } else { @@ -451,27 +464,16 @@ int net_bind(int sockfd, const struct net_sockaddr *addr, int addrlen) { } /* - * 函数功能:启动 TCP Server 监听(多连接模式) - * 入口参数:sockfd - Socket 描述符 (0~7),必须为已绑定的 TCP Socket int - * backlog - 最大并发数据 Socket 数量 int >= 1 - * 返回值:0 - 监听启动成功 int - * -1 - 监听失败(参数无效、状态错误等) int - * 限定条件:Socket 已通过 net_socket() + net_bind() 创建并绑定;type == NET_SOCK_STREAM - * 函数说明:1. 多连接模式下,先为数据 Socket(NET_TCP_SERVER_DATA_SOCK_START ~ 7)配置协议类型、源端口和独立缓冲区 - * 2. CH395F 根据源端口匹配、协议类型为 TCP、状态为 CLOSED 查找可用 Socket 进行自动分配 - * 3. 最后配置监听 Socket 并启动 TCP_LISTEN + * 函数功能:启动 TCP Server 监听(内部版本,必须在 netTask 上下文调用) + * 入口参数:sockfd - Socket 描述符 (0~7) int + * backlog - 数据 Socket 数量(0=单连接模式) int + * 返回值:0 - 成功,-1 - 失败 int + * 限定条件:sockfd 已绑定且为 TCP;netTask 上下文 */ -int net_listen(int sockfd, int backlog) { - net_sock_t *p_sock; +int net_listen_locked(int sockfd, int backlog) { + net_sock_t *p_sock = &s_net_socks[sockfd]; uint8_t status; - /* 参数检�?*/ - if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS) { - s_net_errno = NET_ERR_BADF; - return -1; - } - - p_sock = &s_net_socks[sockfd]; if (!p_sock->in_use || p_sock->type != NET_SOCK_STREAM) { s_net_errno = NET_ERR_INVAL; return -1; @@ -483,25 +485,29 @@ int net_listen(int sockfd, int backlog) { return -1; } - /* 多连接模式:先为数据 Socket 设置协议类型、源端口和独立缓冲区。 - * 必须在监听 Socket OPEN+LISTEN 之前配置,确保 CH395F - * 自动分配机制能扫描到所有可用 Socket。 - * CH395F 根据源端口匹配、协议类型为 TCP、状态为 CLOSED 查找可用 Socket。 */ - for (int ds = NET_TCP_SERVER_DATA_SOCK_START; ds < NET_MAX_SOCKETS; ds++) { - uint8_t base = (uint8_t)(ds * 4); - ch395f_set_send_buf((uint8_t)ds, base, 2); - ch395f_set_recv_buf((uint8_t)ds, base + 2, 2); - /* �?CH395F 手册 9.2.6 节顺序:先设源端口,再设协议类型 */ - ch395f_set_sour_port((uint8_t)ds, p_sock->local_port); - ch395f_set_proto_type((uint8_t)ds, CH395F_PROTO_TYPE_TCP); - s_net_socks[ds].in_use = 1; - s_net_socks[ds].state = NET_SOCK_STATE_TCP_ACCEPT; - s_net_socks[ds].type = NET_SOCK_STREAM; - s_net_socks[ds].ch395_sock = (uint8_t)ds; - s_net_socks[ds].send_ready = 1; + /* 多连接模式:按 backlog 分配数据 Socket */ + if (backlog > 0) { + int max_ds = NET_TCP_SERVER_DATA_SOCK_START + backlog; + if (max_ds > NET_MAX_SOCKETS) { + max_ds = NET_MAX_SOCKETS; + } + for (int ds = NET_TCP_SERVER_DATA_SOCK_START; ds < max_ds; ds++) { + uint8_t base = (uint8_t)(ds * 4); + ch395f_set_send_buf((uint8_t)ds, base, 2); + ch395f_set_recv_buf((uint8_t)ds, base + 2, 2); + ch395f_set_sour_port((uint8_t)ds, p_sock->local_port); + ch395f_set_proto_type((uint8_t)ds, CH395F_PROTO_TYPE_TCP); + s_net_socks[ds].in_use = 1; + s_net_socks[ds].state = NET_SOCK_STATE_TCP_ACCEPT; + s_net_socks[ds].type = NET_SOCK_STREAM; + s_net_socks[ds].ch395_sock = (uint8_t)ds; + s_net_socks[ds].send_ready = 1; + + p_sock->data_socks[p_sock->data_sock_count++] = ds; + } } - /* 最后配置监�?Socket 并启动监�?*/ + /* 配置监听 Socket */ ch395f_set_send_buf((uint8_t)sockfd, 0, 2); ch395f_set_recv_buf((uint8_t)sockfd, 2, 2); ch395f_set_proto_type(sockfd, CH395F_PROTO_TYPE_TCP); @@ -513,10 +519,12 @@ int net_listen(int sockfd, int backlog) { return -1; } - /* 启动 TCP 监听 */ ch395f_tcp_listen(sockfd); p_sock->state = NET_SOCK_STATE_LISTENING; + if (backlog == 0) { + p_sock->standalone_accept = 1; + } for (int j = 0; j < NET_MAX_SOCKETS; j++) { (void)ch395f_get_sock_int_status((uint8_t)j); @@ -527,39 +535,72 @@ int net_listen(int sockfd, int backlog) { } /* - * 函数功能:TCP Server 接受新连接,返回已建立连接的 Socket 描述符 - * 入口参数:sockfd - 监听 Socket 描述符 int - * addr - 输出:客户端地址结构体指针 struct net_sockaddr *(可为 NULL) - * addrlen - 输入输出:地址结构体长度 int *(可为 NULL) - * 返回值:新连接的 Socket 描述符 (1~7),失败返回 -1 int >= 0 || -1 - * 限定条件:Socket 处于 LISTENING 状态;net_poll() 已检测到 CONNECT 事件 - * 函数说明:遍历所有数据 Socket,查找与客户端地址匹配且已建立连接(ESTABLISHED)的 Socket + * 函数功能:启动 TCP Server 监听(线程安全版本,通过消息队列委托给 netTask) + * 入口参数:sockfd - Socket 描述符 int + * backlog - 数据 Socket 数量 int + * 返回值:0 - 成功,-1 - 失败 int + * 限定条件:net_init() + MX_FREERTOS_Init() 已执行;必须在任务上下文中调用 + * 函数说明:通过消息队列委托给 netTask,确保 SPI 串行化 */ -int net_accept(int sockfd, struct net_sockaddr *addr, int *addrlen) { - net_sock_t *p_listen_sock; - int i; +int net_listen(int sockfd, int backlog) { + net_msg_t msg; - /* 参数检�?*/ if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS) { s_net_errno = NET_ERR_BADF; return -1; } + if (netMsgQueueHandle == NULL) { + s_net_errno = NET_ERR; + return -1; + } + + msg.type = NET_MSG_LISTEN; + msg.sockfd = sockfd; + msg.flags = backlog; + msg.caller = xTaskGetCurrentTaskHandle(); + msg.result = -1; + + if (osMessageQueuePut(netMsgQueueHandle, &msg, 0, osWaitForever) != osOK) { + s_net_errno = NET_ERR_BUSY; + return -1; + } + + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + + if (msg.result < 0) { + s_net_errno = NET_ERR; + } + return msg.result; +} + +/* + * 函数功能:TCP Server 接受新连接(内部版本,必须在 netTask 上下文调用) + * 入口参数:sockfd - 监听 Socket 描述符 int + * addr - 输出:客户端地址结构体指针 struct net_sockaddr *(可为 NULL) + * addrlen - 输入输出:地址结构体长度 int *(可为 NULL) + * 返回值:新连接的 Socket 描述符,失败返回 -1 int + * 限定条件:Socket 处于 LISTENING 状态 + * 函数说明:先查 data_socks[] 多连接数据 Socket,再查 standalone_accept 单连接 Socket + */ +static int net_accept_locked(int sockfd, struct net_sockaddr *addr, int *addrlen) { + net_sock_t *p_listen_sock; + int i; + p_listen_sock = &s_net_socks[sockfd]; if (!p_listen_sock->in_use || p_listen_sock->state != NET_SOCK_STATE_LISTENING) { s_net_errno = NET_ERR_INVAL; return -1; } - /* 轮询查找已建立连接的数据 Socket */ net_poll(); + /* 多连接模式:查 data_socks */ for (i = 0; i < p_listen_sock->data_sock_count; i++) { int data_sock = p_listen_sock->data_socks[i]; net_sock_t *p_data_cb = &s_net_socks[data_sock]; if (p_data_cb->state == NET_SOCK_STATE_ESTABLISHED) { - /* 找到已建立连接的 Socket */ if (addr != NULL && addrlen != NULL) { struct net_sockaddr_in *p_addr_in = (struct net_sockaddr_in *)addr; p_addr_in->sin_family = NET_AF_INET; @@ -567,16 +608,73 @@ int net_accept(int sockfd, struct net_sockaddr *addr, int *addrlen) { p_addr_in->sin_addr.s_addr = p_data_cb->remote_ip; *addrlen = sizeof(struct net_sockaddr_in); } - return data_sock; } } - /* 没有新连�?*/ + /* 单连接模式(PASV):查 standalone 监听者是否已 ESTABLISHED */ + if (p_listen_sock->standalone_accept && + p_listen_sock->state == NET_SOCK_STATE_ESTABLISHED) { + if (addr != NULL && addrlen != NULL) { + struct net_sockaddr_in *p_addr_in = (struct net_sockaddr_in *)addr; + p_addr_in->sin_family = NET_AF_INET; + p_addr_in->sin_port = net_htons(p_listen_sock->remote_port); + p_addr_in->sin_addr.s_addr = p_listen_sock->remote_ip; + *addrlen = sizeof(struct net_sockaddr_in); + } + return sockfd; + } + s_net_errno = NET_ERR_WOULDBLOCK; return -1; } +/* + * 函数功能:TCP Server 接受新连接(线程安全版本,通过消息队列委托给 netTask) + * 入口参数:sockfd - 监听 Socket 描述符 int + * addr - 输出:客户端地址结构体指针 struct net_sockaddr *(可为 NULL) + * addrlen - 输入输出:地址结构体长度 int *(可为 NULL) + * 返回值:新连接的 Socket 描述符,失败返回 -1 int + * 限定条件:Socket 处于 LISTENING 状态;net_init() + MX_FREERTOS_Init() 已执行;非 netTask 上下文 + * 函数说明:将接受请求放入消息队列,阻塞等待 netTask 处理完毕 + */ +int net_accept(int sockfd, struct net_sockaddr *addr, int *addrlen) { + net_msg_t msg; + + if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS) { + s_net_errno = NET_ERR_BADF; + return -1; + } + + if (netMsgQueueHandle == NULL) { + s_net_errno = NET_ERR; + return -1; + } + + msg.type = NET_MSG_ACCEPT; + msg.sockfd = sockfd; + msg.buf = (void *)addr; + msg.len = (addrlen != NULL) ? *addrlen : 0; + msg.caller = xTaskGetCurrentTaskHandle(); + msg.result = -1; + + if (osMessageQueuePut(netMsgQueueHandle, &msg, 0, osWaitForever) != osOK) { + s_net_errno = NET_ERR_BUSY; + return -1; + } + + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + + if (addrlen != NULL && addr != NULL) { + *addrlen = sizeof(struct net_sockaddr_in); + } + + if (msg.result < 0) { + s_net_errno = NET_ERR; + } + return msg.result; +} + /* * 函数功能:TCP Client 发起与服务器的连接 * 入口参数:sockfd - Socket 描述符 (0~7),必须为已创建的 TCP Socket int diff --git a/Drivers/BSP/NET/net_socket.h b/Drivers/BSP/NET/net_socket.h index 219d177..73d41b6 100644 --- a/Drivers/BSP/NET/net_socket.h +++ b/Drivers/BSP/NET/net_socket.h @@ -27,6 +27,8 @@ typedef enum { NET_MSG_RECV, NET_MSG_CLOSE, NET_MSG_CONNECT, + NET_MSG_LISTEN, + NET_MSG_ACCEPT, } net_msg_type_t; typedef struct { @@ -318,6 +320,15 @@ int net_recv_sock(net_sock_t *p_sock, void *buf, int len, int flags); */ int net_close_sock(net_sock_t *p_sock); +/* + * 函数功能:启动 TCP Server 监听(内部版本,直接操作,不经过消息队列) + * 入口参数:sockfd - Socket 描述符 + * backlog - 数据 Socket 数量(0=单连接模式) + * 返回值:0 成功,-1 失败 + * 限定条件:必须在 netTask 上下文中调用 + */ +int net_listen_locked(int sockfd, int backlog); + #ifdef __cplusplus } #endif diff --git a/Drivers/BSP/NET/net_types.h b/Drivers/BSP/NET/net_types.h index e3ebf4b..f9e8fd6 100644 --- a/Drivers/BSP/NET/net_types.h +++ b/Drivers/BSP/NET/net_types.h @@ -163,6 +163,9 @@ typedef struct { /* 发送就绪标志 */ uint8_t send_ready; + /* 独立监听标志(PASV 用):该 Socket 为独立监听者,收到 CONNECT 后转为 ESTABLISHED,断开后释放 */ + uint8_t standalone_accept; + /* 统计信息 */ uint32_t recv_bytes; /* 累计接收字节数 */ uint32_t send_bytes; /* 累计发送字节数 */ diff --git a/MDK-ARM/build_log.txt b/MDK-ARM/build_log.txt index a04748a..711d7f8 100644 --- a/MDK-ARM/build_log.txt +++ b/MDK-ARM/build_log.txt @@ -1,30 +1,30 @@ *** Using Compiler 'V5.06 update 7 (build 960)', folder: 'C:\Keil_v5\ARM\ARMCC\Bin' Rebuild target 'STM32F407-Demo' assembling startup_stm32f407xx.s... -compiling ringbuf.c... compiling crc.c... -compiling stm32f4xx_hal_msp.c... -compiling stm32f4xx_it.c... -compiling i2c.c... +compiling ringbuf.c... +compiling dma.c... compiling gpio.c... compiling spi.c... -compiling stm32f4xx_hal_timebase_tim.c... -compiling app_main.c... -compiling sys_clock.c... -compiling dma.c... +compiling stm32f4xx_hal_msp.c... +compiling i2c.c... compiling stm32f4xx_hal_flash_ramfunc.c... -compiling stm32f4xx_hal_gpio.c... -compiling usart.c... -compiling stm32f4xx_hal_flash.c... -compiling stm32f4xx_hal_flash_ex.c... +compiling stm32f4xx_it.c... +compiling sys_clock.c... +compiling stm32f4xx_hal_timebase_tim.c... compiling stm32f4xx_hal_rcc_ex.c... +compiling usart.c... +compiling app_main.c... +compiling stm32f4xx_hal_flash_ex.c... +compiling stm32f4xx_hal_gpio.c... +compiling stm32f4xx_hal_flash.c... compiling rs485_task.c... -compiling net_task.c... compiling adc_task.c... -compiling stm32f4xx_hal_rcc.c... -compiling stm32f4xx_hal_tim_ex.c... +compiling net_task.c... compiling freertos.c... +compiling stm32f4xx_hal_rcc.c... compiling main.c... +compiling stm32f4xx_hal_tim_ex.c... compiling stm32f4xx_hal_tim.c... compiling fdb.c... compiling fdb_kvdb.c... @@ -34,42 +34,42 @@ compiling fal.c... compiling fal_flash.c... compiling fal_partition.c... compiling croutine.c... +compiling stm32f4xx_hal_dma_ex.c... compiling event_groups.c... compiling list.c... -compiling stm32f4xx_hal_dma.c... -compiling system_stm32f4xx.c... compiling stm32f4xx_hal_i2c_ex.c... -compiling stm32f4xx_hal_exti.c... compiling stm32f4xx_hal_pwr.c... +compiling stm32f4xx_hal_dma.c... compiling stm32f4xx_hal_pwr_ex.c... +compiling system_stm32f4xx.c... compiling stm32f4xx_hal_cortex.c... compiling stm32f4xx_hal.c... -compiling sd2506.c... +compiling stm32f4xx_hal_exti.c... compiling rs485.c... -compiling stm32f4xx_hal_dma_ex.c... -compiling fal_flash_gd5f2gq5ue.c... -compiling tpafe5160.c... -compiling ch395f.c... -compiling nand_ftl.c... -compiling gd5f2gq5ue.c... -compiling stm32f4xx_hal_spi.c... -compiling ch395f_test.c... -compiling stm32f4xx_hal_uart.c... compiling queue.c... -compiling net_select.c... +compiling fal_flash_gd5f2gq5ue.c... compiling stream_buffer.c... +compiling ch395f.c... +compiling tpafe5160.c... +compiling sd2506.c... +compiling gd5f2gq5ue.c... compiling timers.c... +compiling stm32f4xx_hal_spi.c... +compiling nand_ftl.c... +compiling net_select.c... compiling heap_4.c... -compiling net_socket.c... -compiling map.c... +compiling stm32f4xx_hal_uart.c... +compiling ch395f_test.c... compiling tasks.c... -compiling journal.c... +compiling map.c... compiling port.c... +compiling journal.c... +compiling net_socket.c... compiling stm32f4xx_hal_i2c.c... compiling ff.c... compiling cmsis_os2.c... linking... -Program Size: Code=58468 RO-data=3488 RW-data=248 ZI-data=54792 +Program Size: Code=58876 RO-data=3560 RW-data=244 ZI-data=54796 FromELF: creating hex file... ".\STM32F407-Demo\STM32F407-Demo.axf" - 0 Error(s), 0 Warning(s). -Build Time Elapsed: 00:00:19 +Build Time Elapsed: 00:00:17 diff --git a/Src/main.c b/Src/main.c index d403531..a0160dd 100644 --- a/Src/main.c +++ b/Src/main.c @@ -64,8 +64,6 @@ rs485_handle_t g_rs485; #define RS485_RX_BUF_SIZE 256 uint8_t g_rs485_rx_buf[RS485_RX_BUF_SIZE]; -/* TCP Server Echo 测试 */ -static int s_listen_sock = -1; /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ @@ -135,26 +133,6 @@ int main(void) /* 启动第一次 RS-485 接收 */ rs485_receive_start(&g_rs485, g_rs485_rx_buf, RS485_RX_BUF_SIZE); - /* BSD Socket API 初始化(包含 CH395F 复位/配置/协议栈初始化/多连接模式) */ - DBG_INFO("net init: %s", - (net_init("192.168.1.100", "255.255.255.0", "192.168.1.1") == 0) ? "OK" : "FAIL"); - - /* 读取 MAC */ - uint8_t mac[6]; - ch395f_get_mac_addr(mac); - DBG_INFO("MAC: %02X:%02X:%02X:%02X:%02X:%02X", - mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - - /* 强制 100M 全双工,建立 PHY 链路 */ - ch395f_set_phy(CH395F_PHY_100M_FULL); - HAL_Delay(3000); - uint8_t phy = ch395f_get_phy_status(); - DBG_INFO("PHY: %s", - (phy == CH395F_PHY_100M_FULL) ? "100M FULL" : - (phy == CH395F_PHY_100M_HALF) ? "100M HALF" : - (phy == CH395F_PHY_10M_FULL) ? "10M FULL" : - (phy == CH395F_PHY_10M_HALF) ? "10M HALF" : - (phy == CH395F_PHY_DISCONN) ? "DISCONNECT" : "UNKNOWN"); /* GD5F 初始化测试 */ int ret = gd5f2gq5ue_init(); @@ -201,30 +179,6 @@ int main(void) sd_vbat / 1000, (sd_vbat % 1000) / 10); } - /* 创建 TCP Server Echo 测试 */ - { - s_listen_sock = net_socket(NET_AF_INET, NET_SOCK_STREAM, 0); - DBG_INFO("TCP listen socket: %d", s_listen_sock); - - struct net_sockaddr_in serv_addr; - memset(&serv_addr, 0, sizeof(serv_addr)); - serv_addr.sin_family = NET_AF_INET; - serv_addr.sin_port = net_htons(8080); - serv_addr.sin_addr.s_addr = NET_INADDR_ANY; - - if (net_bind(s_listen_sock, (struct net_sockaddr *)&serv_addr, sizeof(serv_addr)) == 0) { - DBG_INFO("TCP bind: OK (port 8080)"); - } else { - DBG_ERROR("TCP bind: FAIL"); - } - - if (net_listen(s_listen_sock, NET_TCP_SERVER_MAX_CLIENTS) == 0) { - DBG_INFO("TCP listen: OK (max_clients=%d)", NET_TCP_SERVER_MAX_CLIENTS); - } else { - DBG_ERROR("TCP listen: FAIL"); - } - } - /* 启动第一次转换(CubeMX 已配置 BUSY EXTI 中断) */ tpafe5160_start_conv_irq(); diff --git a/docs/NET_Socket_FTP_Support_Plan.md b/docs/NET_Socket_FTP_Support_Plan.md new file mode 100644 index 0000000..c3c8f5c --- /dev/null +++ b/docs/NET_Socket_FTP_Support_Plan.md @@ -0,0 +1,80 @@ +# NET Socket FTP 支持改造方案 + +## 背景 + +在 net_socket API 上移植 FTP Server(lftpd),需要支持 PASV 模式数据通道。现有 API 仅支持"一个监听 Socket 独占全部数据 Socket"的单 TCP Server 模型,改造后需支持**多监听者 + 动态 Socket 分配**。 + +## Socket 需求量 + +| 用途 | Socket | 说明 | +|------|--------|------| +| 控制通道监听(端口 21) | Socket 0 | 多连接 TCP Server | +| 控制通道数据 | Socket 1 | backlog=1,只接受 1 个 FTP 客户端 | +| PASV 数据监听 + 传输 | Socket 2 | 动态分配,用后释放 | +| 空闲 | Socket 3~7 | 备胎 | + +## 改造项(7 项) + +### A. `net_sock_t` 新增 `standalone_accept` 标志 +- 位置:`net_types.h:net_sock_t` +- 用途:区分多连接数据 Socket 和独立监听者(如 PASV) +- 设 1:PASV 监听者 `handle_connect_event` → 原地转为 `ESTABLISHED` +- 设 1:`handle_disconnect_event` → 释放(`CLOSED`),不恢复 `TCP_ACCEPT` + +### B. `net_listen()` 按 backlog 分配 + 登记 data_socks[] +- 循环从 `NET_TCP_SERVER_DATA_SOCK_START` 到 `backlog` 个 +- 分配的数据 Socket 登记到 `p_sock->data_socks[]`,设 `data_sock_count` +- `backlog == 0` 时:不分配数据 Socket,设 `standalone_accept=1` + +### C. `net_bind()` 支持 port=0 自动分配 +- `sin_port == 0` 时从 49152~65535 分配 +- 静态变量 `s_dynamic_port` 递增 + +### D. `handle_connect_event()` 支持独立监听 +- `LISTENING + standalone_accept`:拷贝远程 IP/Port,状态转 `ESTABLISHED` +- `LISTENING + !standalone_accept`:保持 LISTENING(现有行为) + +### E. `handle_disconnect_event()` 支持独立释放 +- `ESTABLISHED + standalone_accept`:设 `state = CLOSED, in_use = 0`(释放) +- `ESTABLISHED + !standalone_accept`:恢复 `TCP_ACCEPT`(现有行为) +- 释放时不清零整个控制块(正在 `net_recv` 中的任务仍需读取 state) + +### F. `net_accept()` 支持单连接 + 修复多连接 +- 先查 `data_socks[]`(修复现行 bug) +- 再查 `standalone_accept` 监听者是否 `ESTABLISHED` → 返回自身 + +### G. 新增 `net_getsockname()` +- 返回 `local_port` + 本机 IP +- 本机 IP 在 `net_init()` 时缓存(`s_local_ip`),避免 SPI 竞态 + +## 消息队列序列化 + +### 问题 +`net_listen()`、`net_accept()` 直接调用 CH395F SPI,从非 netTask(如 FTP 任务)调用会与 `net_poll()` 冲突。 + +### 方案 +新增消息类型通过现有消息队列串行化: + +| 消息类型 | 用途 | 状态 | +|---------|------|------| +| `NET_MSG_LISTEN` | `net_listen()` 委托 netTask 执行 | 本次实现 | +| `NET_MSG_ACCEPT` | `net_accept()` 委托 netTask 执行 | 本次实现 | + +`net_bind()` 暂不消息化(TCP 部分不碰 SPI,UDP 暂无需跨任务调用)。 + +## 向后兼容 + +- 控制通道 `net_listen(sockfd=0, backlog=7)` 行为不变(仍配满 Socket 1~7) +- `backlog < 7` 时才释放多余 Socket +- `NET_MSG_LISTEN`/`NET_MSG_ACCEPT` 仅新增,不影响现有 `NET_MSG_SEND/RECV/CLOSE/CONNECT` +- `net_sock_t` 新增字段 `standalone_accept` 默认 0,不影响现有使用 + +## 实施顺序 + +1. 消息队列加 `NET_MSG_LISTEN`、`NET_MSG_ACCEPT` +2. `net_listen()` 消息化 + 按 backlog 分配 + data_socks 登记 +3. `net_accept()` 消息化 + 单连接支持 +4. `net_bind()` port=0 支持 +5. `net_types.h` standalone_accept 字段 +6. `handle_connect_event()` / `handle_disconnect_event()` 独立监听逻辑 +7. `net_getsockname()`