Files
STM32F4-Base/Drivers/BSP/NET/net_select.c
2026-07-21 00:30:59 +08:00

298 lines
8.9 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.
/*
* 模块名称Network Select Implementation
* 模块功能I/O 多路复用实现,包括 select 和 poll 两种模式,用于同时监控多个 Socket 的读写事件
* 适用平台STM32F407ZGTx + CH395F 以太网芯片
* 作者:王建锋
* 创建日期2026-07-18
* 修改记录:
* 2026-07-18 王建锋 创建初始版本
*/
#include <string.h>
#include <stdio.h>
#include "cmsis_os.h" /* CMSIS-RTOS2 API: osDelay() */
#include "FreeRTOS.h"
#include "task.h"
#include "net_select.h"
#include "net_socket.h"
#include "ch395f.h"
#include "main.h"
/*
* 私有函数声明区
*/
static int check_socket_readable(int sockfd);
static int check_socket_writable(int sockfd);
static int check_socket_error(int sockfd);
/*
* 公共函数实现区
*/
/*
* 函数功能I/O 多路复用select 模式),等待多个 Socket 的读写事件就绪
* 入口参数nfds - 最大文件描述符数 +1 int > 0
* readfds - 读事件集合指针 net_fd_set *(可为 NULL
* writefds - 写事件集合指针 net_fd_set *(可为 NULL
* exceptfds- 异常事件集合指针 net_fd_set *(可为 NULL
* timeout - 超时时间结构体指针 net_timeval *NULL=无限等待)
* 返回值:就绪的 Socket 数量 int >= 0 || -1
* -1 - 参数无效 int
* 限定条件net_init() + MX_FREERTOS_Init() 已执行;必须在 netTask 上下文中调用
* 函数说明:轮询所有 Socket检查读/写/异常事件是否就绪。阻塞等待直到有事件或超时
*/
int net_select(int nfds, net_fd_set *readfds, net_fd_set *writefds,
net_fd_set *exceptfds, net_timeval *timeout) {
uint32_t start_tick;
uint32_t timeout_ms = 0;
net_fd_set result_read = {0};
net_fd_set result_write = {0};
net_fd_set result_except = {0};
int ready_count = 0;
int i;
/* 计算超时时间 */
if (timeout != NULL) {
timeout_ms = (uint32_t)(timeout->tv_sec * 1000) +
(uint32_t)(timeout->tv_usec / 1000);
}
start_tick = HAL_GetTick();
while (1) {
/* 轮询所<E8AFA2>?Socket */
net_poll();
ready_count = 0;
/* 检查哪<E69FA5>?Socket 有事<E69C89>?*/
for (i = 0; i < nfds && i < NET_MAX_SOCKETS; i++) {
net_sock_t *p_sock = net_get_sock(i);
if (p_sock == NULL) {
continue;
}
/* 检查可读事<E8AFBB>?*/
if (readfds != NULL && NET_FD_ISSET(i, readfds)) {
if (check_socket_readable(i)) {
NET_FD_SET(i, &result_read);
ready_count++;
}
}
/* 检查可写事<E58699>?*/
if (writefds != NULL && NET_FD_ISSET(i, writefds)) {
if (check_socket_writable(i)) {
NET_FD_SET(i, &result_write);
ready_count++;
}
}
/* 检查异常事<E5B8B8>?*/
if (exceptfds != NULL && NET_FD_ISSET(i, exceptfds)) {
if (check_socket_error(i)) {
NET_FD_SET(i, &result_except);
ready_count++;
}
}
}
/* 有事件就绪,返回 */
if (ready_count > 0) {
break;
}
/* 超时检<E697B6>?*/
if (timeout != NULL) {
if (timeout_ms > 0 &&
(HAL_GetTick() - start_tick) >= timeout_ms) {
break; /* 超时返回 0 */
}
}
/* 让出 CPUFreeRTOS osDelay() 会正确进入休眠,允许其他任务运行 */
osDelay(1); /* 1ms 周期轮询,平衡实时性和 CPU 利用率 */
}
/* 复制结果 */
if (readfds != NULL) {
readfds->fd_bits = result_read.fd_bits;
}
if (writefds != NULL) {
writefds->fd_bits = result_write.fd_bits;
}
if (exceptfds != NULL) {
exceptfds->fd_bits = result_except.fd_bits;
}
return ready_count;
}
/*
* 函数功能I/O 多路复用poll 模式),等待多个 Socket 的读写事件就绪
* 入口参数fds - pollfd 数组指针 net_pollfd *
* nfds - 数组元素数量 int > 0
* timeout - 超时时间(毫秒) int >= 0
* 返回值:就绪的 fd 数量 int >= 0 || -1
* -1 - 参数无效 int
* 限定条件net_init() + MX_FREERTOS_Init() 已执行;必须在 netTask 上下文中调用
* 函数说明:轮询所有 Socket检查读/写/错误/挂起事件是否就绪。阻塞等待直到有事件或超时
*/
int net_poll_events(net_pollfd *fds, int nfds, int timeout) {
uint32_t start_tick;
int ready = 0;
int i;
if (fds == NULL || nfds <= 0) {
return -1;
}
start_tick = HAL_GetTick();
while (1) {
/* 轮询 */
net_poll();
ready = 0;
for (i = 0; i < nfds; i++) {
fds[i].revents = 0;
/* 检<>?fd 有效<E69C89>?*/
if (fds[i].fd < 0 || fds[i].fd >= NET_MAX_SOCKETS) {
fds[i].revents |= NET_POLLNVAL;
ready++;
continue;
}
net_sock_t *p_sock = net_get_sock(fds[i].fd);
if (p_sock == NULL) {
fds[i].revents |= NET_POLLNVAL;
ready++;
continue;
}
/* 检查可<E69FA5>?*/
if (fds[i].events & NET_POLLIN) {
if (check_socket_readable(fds[i].fd)) {
fds[i].revents |= NET_POLLIN;
ready++;
}
}
/* 检查可<E69FA5>?*/
if (fds[i].events & NET_POLLOUT) {
if (check_socket_writable(fds[i].fd)) {
fds[i].revents |= NET_POLLOUT;
ready++;
}
}
/* 检查错<E69FA5>?*/
if (check_socket_error(fds[i].fd)) {
fds[i].revents |= NET_POLLERR;
ready++;
}
/* 检查挂起连接断开<E696AD>?*/
if (p_sock->state == NET_SOCK_STATE_TCP_ACCEPT ||
p_sock->state == NET_SOCK_STATE_CLOSED) {
fds[i].revents |= NET_POLLHUP;
ready++;
}
}
if (ready > 0) {
break;
}
/* 超时检<E697B6>?*/
if (timeout >= 0) {
if ((HAL_GetTick() - start_tick) >= (uint32_t)timeout) {
break;
}
}
/* 让出 CPUFreeRTOS osDelay() 会正确进入休眠,允许其他任务运行 */
osDelay(1); /* 1ms 周期轮询,平衡实时性和 CPU 利用率 */
}
return ready;
}
/*
* 私有函数实现<E5AE9E>? */
/*
* 函数功能<EFBC9A>?Socket 是否可读
* 返回值1 可读<E58FAF>? 不可<E4B88D>? */
static int check_socket_readable(int sockfd) {
net_sock_t *p_sock;
uint16_t recv_len;
p_sock = net_get_sock(sockfd);
if (p_sock == NULL) {
return 0;
}
/* 监听 Socket 有新连接时可<E697B6>?*/
if (p_sock->state == NET_SOCK_STATE_LISTENING) {
return 1;
}
/* 已建立连接的 Socket 有数据可<E68DAE>?*/
if (p_sock->state == NET_SOCK_STATE_ESTABLISHED) {
recv_len = ch395f_get_recv_len(sockfd);
if (recv_len > 0) {
return 1;
}
}
return 0;
}
/*
* 函数功能<EFBC9A>?Socket 是否可写
* 返回值1 可写<E58FAF>? 不可<E4B88D>? */
static int check_socket_writable(int sockfd) {
net_sock_t *p_sock;
p_sock = net_get_sock(sockfd);
if (p_sock == NULL) {
return 0;
}
/* 已建立连接的 Socket 发送缓冲区空闲时可<E697B6>?*/
if (p_sock->state == NET_SOCK_STATE_ESTABLISHED) {
if (p_sock->send_ready) {
return 1;
}
}
return 0;
}
/*
* 函数功能<EFBC9A>?Socket 是否有错<E69C89>? * 返回值1 有错误0 无错<E697A0>? */
static int check_socket_error(int sockfd) {
net_sock_t *p_sock;
/* 无效 fd 视为错误 */
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS) {
return 1;
}
p_sock = net_get_sock(sockfd);
if (p_sock == NULL) {
return 1; /* 未使用的 Socket 视为错误 */
}
return 0;
}