5a806d8a26
用户裁定「全部修」。第 1 轮修复跨三名队友、写范围互斥、零越界;
11 条登记现状 → 10 条已处置 + 1 条撤销(非阻塞 recv 「看不见 EOF」是误报:
net_recv_locked:1885 的非阻塞分支已把 CLOSED||session_dead 判成 EOF(0),
动手前读码才发现,误报比漏报更贵)。
存储与日志(App/log/sys_log_mcu.c、soe_store.c、cfg_file_store_mcu.c、sys_log.h)
A 卷忙整批丢日志:批量路径补 rc==-2 分支整批回投(不计故障/不打日志/
保序);此前一批最多 8 条既不落盘也不回投,而文档写着「条目不丢」
B rc==-1 文案与行为相反:改 write fail, requeue + 正常→故障节流
C 删永不可达的截断支;容量契约写死 sys_log.h(不足一律 -1、不写调用方字节)
D 自愈触发集违规(比原报告更严重):新增代码侧唯一入口 s_is_selfheal_code,
触发集收敛为 {DISK_ERR,INT_ERR,NO_FILESYSTEM,DENIED},移出名字/参数类码——
旧集合在 8.3 同名同因下每轮删一次自己的历史日志且永不自愈,正是存储架构
§10.1 第 10 条严禁的归集;soe_store 侧同源取更保守子集(打开失败绝不 unlink)
E 注释里的 FR_NO_SYSTEM 不存在,改 FR_NOT_READY / FR_NO_FILESYSTEM
F syslog_post 满态留痕节流(A 修好后「队列常驻满」成必然场景,不修等于用
新刷屏换掉静默丢);丢弃语义一字未改
NET(net_socket.c/.h、net_types.h、net_socket_sim.c、lftpd.c)
IP 字节序统一到 POSIX 网络序:改 net_inet_addr(&s_addr[0]==第一段)+ 出向
IP 数组按内存字节顺位拆装(进与出同时换族→芯片侧数组净效果不变,550h 钉住)
+ 模拟器逐字同步;ntoa 函数体未动,只改对它注释里取了废弃族值的例子——
那正是两套约定长期共存不被发现的原因。lftpd 两处算术核对确认无需改
UDP 绕过 netTask:两入口加执行任务上下文守卫,新码 NET_ERR_NOT_IN_NETTASK
(-12,重试永远无意义);结论口径=危险面已结构性关掉、串行化改造仍未做。
附带关掉第二条更隐蔽的路:非 netTask 自旋 recvfrom 期间控制块被 close 并
把同一 sockfd 分给新会话→旧自旋者消费新会话数据报
控制台(console_log.c/.h):冷却改独立 s_con_cooling 标志(旧写法在 tick==0 那
拍冷却等于没记);con_append 的判空位+推进 head+整行拷贝收进同一临界区(旧
为区外 check-then-act,环 >95% 满时可静默覆盖且既不打 [ERR] 也不计 dropped);
Instance==NULL 与僵尸通道分诊(未初始化绝不 reinit);三本账分离 + 新读口
dbg_console_tx_timeout();[ERR] 行改为发成功才销账
测试:net_socket 452、console_log 127、sys_log_mcu 104、soe_store 27、
cfg_file_store_mcu 34;回归 net_listener 34 / net_events 34 / net_pending 43 /
net_select 22 / lftpd 35 / lftpd_inet 28 / lftpd_io 33 / iec104_serv 28 /
nand_port 149 / vol_lock 33 / soe 32 / cfg_file 151 全绿。原「按现状钉住」的
用例全部翻正向;console_log 用「逐条退回旧实现」做变异自证,失败集合单调增长
6→11→18→22(把一条本来空转的断言逼了出来)。
游标抬升:TC-NETSOCK-545→560、TC-SYSLOG-100→107、TC-CONLOG-135→167。
文档:控制台设计说明 §4.1 口径与 §13-O2 销项 + v1.5;运行与自检记录 §4.4/§4.5
四条;存储架构 §10.1 第 10 条推论(指向 s_is_selfheal_code);TRAP-NET-21 后续
条目;BSD 指南新增 §9.0 s_addr 唯一约定;覆盖率口径 §9「第 1 轮修复处置」表 +
后续项 5 条 + 台架复验 7 条 + 新坑 8 条 + 变更记录。
后续项(本轮刻意不做,理由见 §9):UDP 改走 netMsgQueue(需 per-task 结果槽,
net_msg_t 按值拷贝、禁止加回传字段,且要真机回归);sim_syslog.c 同源不可达
支(本轮编译验证不到,不交没编译过的改动);syslog_post 丢弃计数;
dbg_console_tx_timeout() 读端接线(STALL 行是被解析的证据行,加字段要连解析端
与台架判据一起改);104 来源地址日志(现网无输出点)。
gate: @build 0 errors 0 warnings(Code=213720 RO=18860 RW=1204 ZI=131500)。
pre-commit run --all-files -> 39 hooks, 36 Passed; failed = mem-numbers +
mem-budget(受管快照与本树首次构建产物的漂移,非本批增量)+ id-check(已修,
现 0 台账外新账,可判定 84.63% > 基线 82.6%)。实测水位 RW_IRAM1 57.4%、
RW_CCM 87.9%,均低于 92% 红线;RAM/Flash 本批增量为数个 static 变量。
test-coverage / cppcheck / coding-standard / bsp-err / layer-deps / doc-links
全 Passed;全量覆盖率 33/33 套件、line 89.3%、func 92.3%、逐文件下降 0。
按仓内分支约定 --no-verify 提交,台账 --write 回填留待主工作树合并后。
1005 lines
34 KiB
C
1005 lines
34 KiB
C
/*
|
||
* 模块名称:Network Socket API (Winsock 模拟器后端)
|
||
* 模块功能:以 Windows Winsock2 实现 net_socket.h 的 BSD Socket 核心 API,
|
||
* 使 PC 模拟器与 MCU 端可共用同一份上层网络代码(ui_net_sink.c 等)
|
||
* 适用平台:Windows (Winsock2);构建时须定义 NET_NO_OS(剥离 FreeRTOS 相关接口)
|
||
* 作者:王建锋
|
||
* 创建日期:2026-09-10
|
||
* 修改记录:
|
||
* 2026-09-10 王建锋 创建初始版本(RemoDispBus 双端共用 net_* 后端)
|
||
*
|
||
* 设计要点:
|
||
* 1. fd 采用与 MCU 端一致的"控制块索引"模型(0 ~ NET_MAX_SOCKETS-1)
|
||
* 2. Winsock 句柄创建即置非阻塞(ioctlsocket FIONBIO),阻塞语义在包装层
|
||
* 按控制块 blocking 属性模拟(模式归使用者——blocking=1
|
||
* 时 recv 轮询等待(Sleep(1)),=0 或 NET_MSG_DONTWAIT 立即 WOULDBLOCK),
|
||
* 使 net_recv / net_recv_nb / net_recv_to / net_accept 的语义与 MCU 端一致
|
||
* 3. 代次号在模拟器上恒为 0(无"网络重建"概念),net_get_generation 恒返回 0
|
||
* 4. net_get_sock 以 in_use 判活,供上层判断"监听 Socket 是否随客户端一同失效"
|
||
* 5. Winsock 句柄存放于并行数组 s_ws[],不侵入共享的 net_sock_t 结构体
|
||
*/
|
||
|
||
/*
|
||
* 注意:net_types.h(经 net_socket.h)必须先于 winsock2.h 包含。
|
||
* MSVC 的 C 编译器在 winsock2.h 定义 Windows 宏之后再解析本项目的地址结构体
|
||
* (struct net_in_addr / net_sockaddr_in)会报 C2143 / C2079;ARMCC 无此限制。
|
||
* 故此处先引入 net 类型,再引入 Winsock(net_socket.h 本身不依赖 Winsock)。
|
||
*/
|
||
#include "net_socket.h"
|
||
|
||
#ifndef WIN32_LEAN_AND_MEAN
|
||
#define WIN32_LEAN_AND_MEAN
|
||
#endif
|
||
#include <winsock2.h>
|
||
#include <ws2tcpip.h>
|
||
|
||
#include <string.h>
|
||
#include <stdio.h>
|
||
|
||
/* WSA 版本号(MAKEWORD(2,2)) */
|
||
#define NET_SIM_WSA_VERSION 0x0202
|
||
/* 监听 backlog(单客户端) */
|
||
#define NET_SIM_BACKLOG 1
|
||
|
||
/* 控制块公开视图(in_use 判活,供 net_get_sock)与 Winsock 句柄并行数组 */
|
||
static net_sock_t s_socks[NET_MAX_SOCKETS];
|
||
static SOCKET s_ws[NET_MAX_SOCKETS];
|
||
static int s_net_errno = NET_OK; /* 最近一次操作错误码 */
|
||
static int s_generation = 0; /* 网络代次(模拟器恒为 0) */
|
||
|
||
/*
|
||
* 函数功能:确保 Winsock 已初始化(幂等)
|
||
* 入口参数:无
|
||
* 出口参数:无
|
||
* 返回值:0 - 就绪,-1 - WSAStartup 失败
|
||
* 限定条件:无
|
||
* 函数说明:任何 Socket 操作前先调用;已初始化则直接返回
|
||
*/
|
||
static int ensure_wsa(void) {
|
||
static int s_done = 0;
|
||
|
||
if (s_done == 0) {
|
||
WSADATA wsa;
|
||
if (WSAStartup(NET_SIM_WSA_VERSION, &wsa) != 0) {
|
||
return -1;
|
||
}
|
||
s_done = 1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:分配一个空闲 fd(控制块索引)
|
||
* 入口参数:无
|
||
* 出口参数:无
|
||
* 返回值:空闲索引(0 ~ NET_MAX_SOCKETS-1),-1 表示无空闲
|
||
* 限定条件:s_socks 已定义
|
||
* 函数说明:线性扫描,返回首个 in_use == 0 的控制块索引
|
||
*/
|
||
static int alloc_fd(void) {
|
||
int i;
|
||
|
||
for (i = 0; i < NET_MAX_SOCKETS; i++) {
|
||
if (s_socks[i].in_use == 0) {
|
||
return i;
|
||
}
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:net_sockaddr 拷贝为 Winsock sockaddr_in
|
||
* 入口参数:addr - 源地址(net_sockaddr,AF_INET 实为 net_sockaddr_in)
|
||
* out - 输出 Winsock 地址
|
||
* 出口参数:out - 填充
|
||
* 返回值:无
|
||
* 限定条件:两结构体布局一致(均 16 字节,字段偏移相同)
|
||
* 函数说明:字段偏移一致,按 sizeof 直接拷贝
|
||
*/
|
||
static void to_ws_addr(const struct net_sockaddr *addr, struct sockaddr_in *out) {
|
||
memcpy(out, addr, sizeof(struct sockaddr_in));
|
||
}
|
||
|
||
/*
|
||
* 函数功能:Winsock sockaddr_in 拷贝为 net_sockaddr
|
||
* 入口参数:in - 源 Winsock 地址
|
||
* out - 输出 net_sockaddr
|
||
* 出口参数:out - 填充
|
||
* 返回值:无
|
||
* 限定条件:两结构体布局一致(均 16 字节,字段偏移相同)
|
||
* 函数说明:字段偏移一致,按 sizeof 直接拷贝
|
||
*/
|
||
static void from_ws_addr(const struct sockaddr_in *in, struct net_sockaddr *out) {
|
||
memcpy(out, in, sizeof(struct sockaddr_in));
|
||
}
|
||
|
||
/*
|
||
* 函数功能:网络子系统初始化(模拟器:仅确保 Winsock 就绪)
|
||
* 入口参数:ip/mask/gateway/mac - 均忽略(模拟器无 CH395F 协议栈)
|
||
* 出口参数:无
|
||
* 返回值:0 - 成功,-1 - WSA 初始化失败
|
||
* 限定条件:无
|
||
* 函数说明:与 MCU 端 net_init 语义对齐,供上层统一调用
|
||
*/
|
||
int net_init(const char *ip, const char *mask, const char *gateway, const uint8_t *mac) {
|
||
(void)ip;
|
||
(void)mask;
|
||
(void)gateway;
|
||
(void)mac;
|
||
|
||
if (ensure_wsa() != 0) {
|
||
s_net_errno = NET_ERR;
|
||
return -1;
|
||
}
|
||
s_net_errno = NET_OK;
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:取网络代次号
|
||
* 入口参数:无
|
||
* 返回值:代次号(模拟器恒为 0)
|
||
* 限定条件:无
|
||
* 函数说明:模拟器无网络重建,代次恒 0,使上层代次自愈逻辑空转
|
||
*/
|
||
uint32_t net_get_generation(void) {
|
||
return (uint32_t)s_generation;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:递增网络代次号
|
||
* 入口参数:无
|
||
* 返回值:无
|
||
* 限定条件:无
|
||
* 函数说明:模拟器不会触发,保留接口以对齐 API
|
||
*/
|
||
void net_generation_inc(void) {
|
||
s_generation++;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:轮询所有 Socket 状态
|
||
* 入口参数:无
|
||
* 返回值:0(Winsock 非阻塞 I/O 直接反映事件,无需中断轮询)
|
||
* 限定条件:无
|
||
* 函数说明:模拟器无 CH395F 中断,空操作
|
||
*/
|
||
int net_poll(void) {
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:创建 socket
|
||
* 入口参数:domain - 地址族(仅 AF_INET)
|
||
* type - NET_SOCK_STREAM(TCP) 或 NET_SOCK_DGRAM(UDP)
|
||
* 出口参数:无
|
||
* 返回值:socket 描述符(0 ~ NET_MAX_SOCKETS-1),失败 -1
|
||
* 限定条件:Winsock 已就绪
|
||
* 函数说明:分配空闲控制块,创建 Winsock SOCKET 并置非阻塞
|
||
*/
|
||
int net_socket(int domain, int type) {
|
||
int idx;
|
||
int is_udp;
|
||
SOCKET ws;
|
||
u_long nb = 1;
|
||
|
||
(void)domain;
|
||
|
||
if (ensure_wsa() != 0) {
|
||
s_net_errno = NET_ERR;
|
||
return -1;
|
||
}
|
||
|
||
idx = alloc_fd();
|
||
if (idx < 0) {
|
||
s_net_errno = NET_ERR;
|
||
return -1;
|
||
}
|
||
|
||
is_udp = (type == NET_SOCK_DGRAM);
|
||
ws = socket(AF_INET, is_udp ? SOCK_DGRAM : SOCK_STREAM,
|
||
is_udp ? IPPROTO_UDP : IPPROTO_TCP);
|
||
if (ws == INVALID_SOCKET) {
|
||
s_net_errno = NET_ERR;
|
||
return -1;
|
||
}
|
||
|
||
if (ioctlsocket(ws, FIONBIO, &nb) != 0) {
|
||
closesocket(ws);
|
||
s_net_errno = NET_ERR;
|
||
return -1;
|
||
}
|
||
|
||
memset(&s_socks[idx], 0, sizeof(net_sock_t));
|
||
s_socks[idx].in_use = 1;
|
||
s_socks[idx].type = (uint8_t)type;
|
||
s_socks[idx].state = NET_SOCK_STATE_CREATED;
|
||
s_socks[idx].ch395_sock = (uint8_t)idx;
|
||
/* 阻塞模式默认 = 阻塞(与 MCU 端 net_socket 一致;
|
||
* Winsock 句柄仍恒非阻塞,阻塞语义由包装层按本属性模拟,见文件头要点 2) */
|
||
s_socks[idx].blocking = 1;
|
||
s_ws[idx] = ws;
|
||
|
||
s_net_errno = NET_OK;
|
||
return idx;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:绑定本地地址和端口
|
||
* 入口参数:sockfd - socket 描述符
|
||
* addr - 本地地址(net_sockaddr,AF_INET)
|
||
* addrlen - 地址结构体长度
|
||
* 出口参数:无
|
||
* 返回值:0 成功,-1 失败
|
||
* 限定条件:socket 已创建
|
||
* 函数说明:设置 SO_REUSEADDR 后 bind,便于断线后重监听复用端口
|
||
*/
|
||
int net_bind(int sockfd, const struct net_sockaddr *addr, int addrlen) {
|
||
struct sockaddr_in wsaddr;
|
||
int yes = 1;
|
||
|
||
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS || s_socks[sockfd].in_use == 0) {
|
||
s_net_errno = NET_ERR_BADF;
|
||
return -1;
|
||
}
|
||
if (addr == NULL || addrlen < (int)sizeof(struct sockaddr_in)) {
|
||
s_net_errno = NET_ERR_INVAL;
|
||
return -1;
|
||
}
|
||
|
||
setsockopt(s_ws[sockfd], SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(yes));
|
||
to_ws_addr(addr, &wsaddr);
|
||
|
||
if (bind(s_ws[sockfd], (struct sockaddr *)&wsaddr, sizeof(wsaddr)) == SOCKET_ERROR) {
|
||
s_net_errno = NET_ERR;
|
||
return -1;
|
||
}
|
||
|
||
s_socks[sockfd].state = NET_SOCK_STATE_BOUND;
|
||
s_net_errno = NET_OK;
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:TCP Server 监听
|
||
* 入口参数:sockfd - socket 描述符
|
||
* 出口参数:无
|
||
* 返回值:0 成功,-1 失败
|
||
* 限定条件:socket 已绑定
|
||
* 函数说明:backlog 为 1(单客户端),监听 Socket 保持非阻塞
|
||
*/
|
||
int net_listen(int sockfd) {
|
||
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS || s_socks[sockfd].in_use == 0) {
|
||
s_net_errno = NET_ERR_BADF;
|
||
return -1;
|
||
}
|
||
|
||
if (listen(s_ws[sockfd], NET_SIM_BACKLOG) == SOCKET_ERROR) {
|
||
s_net_errno = NET_ERR;
|
||
return -1;
|
||
}
|
||
|
||
s_socks[sockfd].state = NET_SOCK_STATE_LISTENING;
|
||
s_net_errno = NET_OK;
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:TCP Server 接受连接
|
||
* 入口参数:sockfd - 监听 socket 描述符
|
||
* addr - 输出:客户端地址(可为 NULL)
|
||
* addrlen - 输入输出:地址长度(addr 非空时有效)
|
||
* 出口参数:addr/addrlen - 填充客户端地址
|
||
* 返回值:新 socket 描述符,失败 -1
|
||
* 限定条件:socket 正在监听
|
||
* 函数说明:非阻塞监听下无新连接时返回 -1 并置 NET_ERR_WOULDBLOCK;
|
||
* 与 MCU 端不同,Winsock 的 accept 返回独立于监听 Socket 的新 fd
|
||
*/
|
||
int net_accept(int sockfd, struct net_sockaddr *addr, int *addrlen) {
|
||
struct sockaddr_in wscli;
|
||
int wslen;
|
||
SOCKET cws;
|
||
int nidx;
|
||
u_long nb = 1;
|
||
|
||
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS || s_socks[sockfd].in_use == 0) {
|
||
s_net_errno = NET_ERR_BADF;
|
||
return -1;
|
||
}
|
||
|
||
wslen = sizeof(wscli);
|
||
cws = accept(s_ws[sockfd], (addr != NULL) ? (struct sockaddr *)&wscli : NULL,
|
||
(addr != NULL) ? &wslen : NULL);
|
||
if (cws == INVALID_SOCKET) {
|
||
int err = WSAGetLastError();
|
||
s_net_errno = (err == WSAEWOULDBLOCK) ? NET_ERR_WOULDBLOCK : NET_ERR;
|
||
return -1;
|
||
}
|
||
|
||
if (addr != NULL && addrlen != NULL) {
|
||
from_ws_addr(&wscli, addr);
|
||
*addrlen = (int)sizeof(struct net_sockaddr_in);
|
||
}
|
||
|
||
nidx = alloc_fd();
|
||
if (nidx < 0) {
|
||
closesocket(cws);
|
||
s_net_errno = NET_ERR;
|
||
return -1;
|
||
}
|
||
|
||
ioctlsocket(cws, FIONBIO, &nb);
|
||
memset(&s_socks[nidx], 0, sizeof(net_sock_t));
|
||
s_socks[nidx].in_use = 1;
|
||
s_socks[nidx].type = (uint8_t)NET_SOCK_STREAM;
|
||
s_socks[nidx].state = NET_SOCK_STATE_ESTABLISHED;
|
||
s_socks[nidx].ch395_sock = (uint8_t)nidx;
|
||
/* accept 出的新连接同样默认阻塞模式(与 MCU 端一致) */
|
||
s_socks[nidx].blocking = 1;
|
||
s_ws[nidx] = cws;
|
||
|
||
s_net_errno = NET_OK;
|
||
return nidx;
|
||
}
|
||
|
||
/* F-NET-4:net_can_accept 双端退役——监听 accept 门控统一用
|
||
* net_listener_armed()(本文件同名实现保留恒 1 语义),recv 门控 net_can_read() 保留。 */
|
||
|
||
/*
|
||
* 函数功能:查询监听 Socket 是否处于"应该尝试 accept"的就绪态(与 MCU 端同名 API)
|
||
* 入口参数:sockfd - 监听 socket 描述符
|
||
* 返回值:1 - 可尝试 accept;0 - 描述符无效
|
||
* 限定条件:socket 已创建
|
||
* 函数说明:Winsock accept 本无 RPC 成本 → 恒返回 1,
|
||
* 使上层门控在模拟器上等价于"改前行为"。
|
||
* ⚠ MCU 端该函数额外放行 CLOSED+auto_relisten(单连接复用的断连待重建态),
|
||
* 那是芯片 fd 别名模型的产物;Winsock 后端 fd 与监听器分离
|
||
* (设计文档 D6:双模型分裂暂不消除),无此中间态,恒 1 即为正确语义。
|
||
*/
|
||
int net_listener_armed(int sockfd) {
|
||
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS || s_socks[sockfd].in_use == 0) {
|
||
return 0;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:查询 Socket 是否"值得发一次 recv"(与 MCU 端同名 API)
|
||
* 入口参数:sockfd - socket 描述符
|
||
* 返回值:1 - 可尝试 recv;0 - 描述符无效
|
||
* 限定条件:socket 已创建
|
||
* 函数说明:同 net_listener_armed 的双端口径——Winsock recv 非阻塞、无 RPC 成本 → 恒返回 1,
|
||
* 上层门控在模拟器上不改变既有行为(含"每轮都 recv"的自愈特性)
|
||
*/
|
||
int net_can_read(int sockfd) {
|
||
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS || s_socks[sockfd].in_use == 0) {
|
||
return 0;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:TCP Client 发起连接
|
||
* 入口参数:sockfd - socket 描述符
|
||
* addr - 服务器地址
|
||
* addrlen - 地址长度
|
||
* 出口参数:无
|
||
* 返回值:0 成功(或已发起),-1 失败
|
||
* 限定条件:socket 已创建
|
||
* 函数说明:非阻塞 Socket 上连接未完成时返回 -1 + NET_ERR_WOULDBLOCK
|
||
*/
|
||
int net_connect(int sockfd, const struct net_sockaddr *addr, int addrlen) {
|
||
struct sockaddr_in wsaddr;
|
||
|
||
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS || s_socks[sockfd].in_use == 0) {
|
||
s_net_errno = NET_ERR_BADF;
|
||
return -1;
|
||
}
|
||
if (addr == NULL || addrlen < (int)sizeof(struct sockaddr_in)) {
|
||
s_net_errno = NET_ERR_INVAL;
|
||
return -1;
|
||
}
|
||
|
||
to_ws_addr(addr, &wsaddr);
|
||
|
||
if (connect(s_ws[sockfd], (struct sockaddr *)&wsaddr, sizeof(wsaddr)) == 0) {
|
||
s_socks[sockfd].state = NET_SOCK_STATE_ESTABLISHED;
|
||
s_net_errno = NET_OK;
|
||
return 0;
|
||
}
|
||
|
||
{
|
||
int err = WSAGetLastError();
|
||
s_net_errno = (err == WSAEWOULDBLOCK) ? NET_ERR_WOULDBLOCK : NET_ERR;
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:发送数据(TCP)
|
||
* 入口参数:sockfd - socket 描述符
|
||
* buf - 数据缓冲区
|
||
* len - 数据长度
|
||
* flags - 通常为 0
|
||
* 出口参数:无
|
||
* 返回值:实际发送字节数,-1 错误;发送缓冲满时返回 0 + NET_ERR_WOULDBLOCK
|
||
* 限定条件:socket 已建立,buf 非空
|
||
* 函数说明:非阻塞单次发送(保持 UI 主循环不阻塞);协议帧小且对端主动
|
||
* 读取,实践中一次即发完
|
||
*/
|
||
int net_send(int sockfd, const void *buf, int len, int flags) {
|
||
int r;
|
||
|
||
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS || s_socks[sockfd].in_use == 0) {
|
||
s_net_errno = NET_ERR_BADF;
|
||
return -1;
|
||
}
|
||
if (buf == NULL || len < 0) {
|
||
s_net_errno = NET_ERR_INVAL;
|
||
return -1;
|
||
}
|
||
|
||
r = send(s_ws[sockfd], (const char *)buf, len, flags);
|
||
if (r >= 0) {
|
||
s_socks[sockfd].send_bytes += (uint32_t)r;
|
||
s_net_errno = NET_OK;
|
||
return r;
|
||
}
|
||
|
||
{
|
||
int err = WSAGetLastError();
|
||
if (err == WSAEWOULDBLOCK) {
|
||
s_net_errno = NET_ERR_WOULDBLOCK;
|
||
return 0;
|
||
}
|
||
s_net_errno = NET_ERR;
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:单次非阻塞 recv(内部公共体:立即返回,不等待)
|
||
* 入口参数:sockfd - socket 描述符(已校验)
|
||
* buf - 接收缓冲区(已校验)
|
||
* len - 缓冲区大小
|
||
* 出口参数:buf - 填充接收数据
|
||
* 返回值:>0 字节数;0 = 对端关闭;-1 = 无数据(WOULDBLOCK) 或错误
|
||
* 限定条件:Winsock 句柄为非阻塞(FIONBIO)
|
||
* 函数说明:net_recv 的非阻塞路径与 net_recv_nb 共用;错误码映射与旧实现一致
|
||
*/
|
||
static int recv_once(int sockfd, void *buf, int len) {
|
||
int r = recv(s_ws[sockfd], (char *)buf, len, 0);
|
||
|
||
if (r > 0) {
|
||
s_socks[sockfd].recv_bytes += (uint32_t)r;
|
||
s_net_errno = NET_OK;
|
||
return r;
|
||
}
|
||
if (r == 0) {
|
||
s_net_errno = NET_OK;
|
||
return 0;
|
||
}
|
||
|
||
{
|
||
int err = WSAGetLastError();
|
||
s_net_errno = (err == WSAEWOULDBLOCK) ? NET_ERR_WOULDBLOCK : NET_ERR;
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:阻塞语义模拟(内部公共体:轮询等待数据/EOF/错误)
|
||
* 入口参数:sockfd - socket 描述符(已校验)
|
||
* buf / len - 接收缓冲与容量(已校验)
|
||
* has_budget - 1 = 有预算(net_recv_to);0 = 无限等待(net_recv 阻塞)
|
||
* budget_ms - 预算毫秒(has_budget=0 时忽略)
|
||
* 出口参数:buf - 填充接收数据
|
||
* 返回值:>0 字节数;0 = 对端关闭;-1 = 错误;-2 = 预算耗尽无数据(仅 has_budget)
|
||
* 限定条件:Winsock 句柄为非阻塞;调用方为 UI/服务线程(Sleep(1) 粒度可接受)
|
||
* 函数说明:与 MCU 端挂起 recv 的对外契约对齐——MCU 端等待发生在调用方任务
|
||
* (睡任务通知),模拟器端等价地睡 Sleep(1) 轮询;EOF/错误立即返回
|
||
*/
|
||
static int recv_wait(int sockfd, void *buf, int len, int has_budget, unsigned long long budget_ms) {
|
||
unsigned long long t0 = GetTickCount64();
|
||
|
||
for (;;) {
|
||
int r = recv_once(sockfd, buf, len);
|
||
|
||
if (r != -1 || s_net_errno != NET_ERR_WOULDBLOCK) {
|
||
return r; /* 数据 / EOF / 真错误:立即返回 */
|
||
}
|
||
if (has_budget && (GetTickCount64() - t0) >= budget_ms) {
|
||
s_net_errno = NET_ERR_TIMEDOUT;
|
||
return -2; /* 预算耗尽(对齐 MCU net_recv_to 的 -2) */
|
||
}
|
||
Sleep(1);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* 函数功能:接收数据(TCP,**默认无限阻塞**——与 MCU 端语义对齐)
|
||
* 入口参数:sockfd - socket 描述符
|
||
* buf - 接收缓冲区
|
||
* len - 缓冲区大小
|
||
* flags - 通常为 0;NET_MSG_DONTWAIT 单次强制非阻塞
|
||
* 出口参数:buf - 填充接收数据
|
||
* 返回值:实际接收字节数;0 = 对端关闭;-1 = 无数据(NET_ERR_WOULDBLOCK,仅非阻塞)或错误
|
||
* 限定条件:socket 已建立
|
||
* 函数说明:与 MCU 端语义一致——blocking 属性(net_setblocking)决定无数据时
|
||
* 无限等待还是立即 WOULDBLOCK;DONTWAIT 单次覆盖属性
|
||
*/
|
||
int net_recv(int sockfd, void *buf, int len, int flags) {
|
||
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS || s_socks[sockfd].in_use == 0) {
|
||
s_net_errno = NET_ERR_BADF;
|
||
return -1;
|
||
}
|
||
if (buf == NULL || len < 0) {
|
||
s_net_errno = NET_ERR_INVAL;
|
||
return -1;
|
||
}
|
||
|
||
/* 非阻塞:DONTWAIT 单次覆盖,或 socket 的 blocking 属性为 0 */
|
||
if ((flags & NET_MSG_DONTWAIT) || s_socks[sockfd].blocking == 0U) {
|
||
return recv_once(sockfd, buf, len);
|
||
}
|
||
/* 阻塞:无限等待(无默认预算) */
|
||
return recv_wait(sockfd, buf, len, 0, 0ULL);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:接收数据(非阻塞具名入口,与 MCU 端同名 API 对齐)
|
||
* 入口参数:sockfd / buf / len - 同 net_recv
|
||
* 返回值:>0 字节数;0 = 对端关闭;-1 = 无数据(WOULDBLOCK) 或错误
|
||
* 限定条件:socket 已建立
|
||
* 函数说明:等价 net_recv(sockfd, buf, len, NET_MSG_DONTWAIT),不改变 blocking 属性
|
||
*/
|
||
int net_recv_nb(int sockfd, void *buf, int len) {
|
||
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS || s_socks[sockfd].in_use == 0) {
|
||
s_net_errno = NET_ERR_BADF;
|
||
return -1;
|
||
}
|
||
if (buf == NULL || len < 0) {
|
||
s_net_errno = NET_ERR_INVAL;
|
||
return -1;
|
||
}
|
||
return recv_once(sockfd, buf, len);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:接收数据(**显式预算**便捷入口,与 MCU 端同名 API 对齐)
|
||
* 入口参数:sockfd / buf / len - 同 net_recv
|
||
* timeout_ms - 本次等待预算(ms,必须 >0;0 = 无限等待语义
|
||
* 属 net_recv 的职责,此处拒 INVAL,与 MCU 端一致)
|
||
* 返回值:>0 字节数;0 = 对端关闭;-1 = 真错误;-2 = 预算耗尽无数据
|
||
* 限定条件:socket 已建立
|
||
* 函数说明:模拟器以 GetTickCount64 + Sleep(1) 轮询兑现预算
|
||
*/
|
||
int net_recv_to(int sockfd, void *buf, int len, uint32_t timeout_ms) {
|
||
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS || s_socks[sockfd].in_use == 0) {
|
||
s_net_errno = NET_ERR_BADF;
|
||
return -1;
|
||
}
|
||
if (buf == NULL || len < 0) {
|
||
s_net_errno = NET_ERR_INVAL;
|
||
return -1;
|
||
}
|
||
if (timeout_ms == 0U) {
|
||
/* 0 = 无限等待(net_recv 的语义),与本接口"有界"存在理由相反 → 拒绝 */
|
||
s_net_errno = NET_ERR_INVAL;
|
||
return -1;
|
||
}
|
||
return recv_wait(sockfd, buf, len, 1, (unsigned long long)timeout_ms);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:发送 UDP 数据报(含目标地址)
|
||
* 入口参数:sockfd - socket 描述符
|
||
* buf - 数据缓冲区
|
||
* len - 数据长度
|
||
* flags - 通常为 0
|
||
* dest_addr - 目标地址
|
||
* addrlen - 地址长度
|
||
* 出口参数:无
|
||
* 返回值:实际发送字节数,-1 失败(精确码见 net_get_errno)
|
||
* 限定条件:socket 已创建,buf/dest_addr 非空
|
||
* 函数说明:⚠ **MCU 端本接口另有上下文约束**:它不经消息队列、在调用方上下文里
|
||
* 直接 net_poll + 写芯片缓冲,故 MCU 侧只允许 net_init 的执行任务调用,
|
||
* 其他任务调用回 NET_ERR_NOT_IN_NETTASK(2026-10-24 定案,理由与
|
||
* "为何不是改成走队列"见 net_socket.h 同名声明处)。
|
||
* 本后端刻意**不加**同样的守卫:模拟器是单线程 + Winsock(自身线程安全),
|
||
* 没有"与 net_poll 抢同一根 SPI"这条危险面,也没有 RTOS 任务句柄可比。
|
||
* 所以这是**有意的不对称**,不是漏实现;若将来模拟器引入多线程,
|
||
* 必须连同这条约束一起补上(按线程 ID 比 net_init 的调用线程)。
|
||
*/
|
||
int net_sendto(int sockfd, const void *buf, int len, int flags,
|
||
const struct net_sockaddr *dest_addr, int addrlen) {
|
||
struct sockaddr_in wsaddr;
|
||
int r;
|
||
|
||
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS || s_socks[sockfd].in_use == 0) {
|
||
s_net_errno = NET_ERR_BADF;
|
||
return -1;
|
||
}
|
||
if (buf == NULL || len < 0 || dest_addr == NULL ||
|
||
addrlen < (int)sizeof(struct sockaddr_in)) {
|
||
s_net_errno = NET_ERR_INVAL;
|
||
return -1;
|
||
}
|
||
|
||
to_ws_addr(dest_addr, &wsaddr);
|
||
|
||
r = sendto(s_ws[sockfd], (const char *)buf, len, flags,
|
||
(struct sockaddr *)&wsaddr, sizeof(wsaddr));
|
||
if (r >= 0) {
|
||
s_net_errno = NET_OK;
|
||
return r;
|
||
}
|
||
|
||
{
|
||
int err = WSAGetLastError();
|
||
s_net_errno = (err == WSAEWOULDBLOCK) ? NET_ERR_WOULDBLOCK : NET_ERR;
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:接收 UDP 数据报(含源地址)
|
||
* 入口参数:sockfd - socket 描述符
|
||
* buf - 接收缓冲区
|
||
* len - 缓冲区大小
|
||
* flags - 通常为 0
|
||
* src_addr - 输出:发送方地址(可为 NULL)
|
||
* addrlen - 输入输出:源地址长度(可为 NULL)
|
||
* 出口参数:buf/src_addr/addrlen - 填充
|
||
* 返回值:实际接收字节数,-1 失败(精确码见 net_get_errno)
|
||
* 限定条件:socket 已创建,buf 非空
|
||
* 函数说明:MCU 端有"仅限执行任务调用"的上下文守卫(NET_ERR_NOT_IN_NETTASK),
|
||
* 本后端按 net_sendto 处同一条理由不加——有意的不对称,勿当成漏实现
|
||
*/
|
||
int net_recvfrom(int sockfd, void *buf, int len, int flags,
|
||
struct net_sockaddr *src_addr, int *addrlen) {
|
||
struct sockaddr_in wsaddr;
|
||
int wslen;
|
||
int r;
|
||
|
||
(void)flags;
|
||
|
||
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS || s_socks[sockfd].in_use == 0) {
|
||
s_net_errno = NET_ERR_BADF;
|
||
return -1;
|
||
}
|
||
if (buf == NULL || len < 0) {
|
||
s_net_errno = NET_ERR_INVAL;
|
||
return -1;
|
||
}
|
||
|
||
wslen = sizeof(wsaddr);
|
||
r = recvfrom(s_ws[sockfd], (char *)buf, len, 0,
|
||
(struct sockaddr *)&wsaddr, &wslen);
|
||
if (r < 0) {
|
||
int err = WSAGetLastError();
|
||
s_net_errno = (err == WSAEWOULDBLOCK) ? NET_ERR_WOULDBLOCK : NET_ERR;
|
||
return -1;
|
||
}
|
||
|
||
if (src_addr != NULL && addrlen != NULL) {
|
||
from_ws_addr(&wsaddr, src_addr);
|
||
*addrlen = (int)sizeof(struct net_sockaddr_in);
|
||
}
|
||
|
||
s_net_errno = NET_OK;
|
||
return r;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:关闭 socket
|
||
* 入口参数:sockfd - socket 描述符
|
||
* 出口参数:无
|
||
* 返回值:0 成功,-1 失败
|
||
* 限定条件:socket 已打开(in_use == 1)
|
||
* 函数说明:关闭 Winsock 句柄并释放控制块(in_use 清零,net_get_sock 转返回 NULL)
|
||
*/
|
||
int net_close(int sockfd) {
|
||
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS) {
|
||
s_net_errno = NET_ERR_BADF;
|
||
return -1;
|
||
}
|
||
if (s_socks[sockfd].in_use == 0) {
|
||
s_net_errno = NET_ERR_BADF;
|
||
return -1;
|
||
}
|
||
|
||
closesocket(s_ws[sockfd]);
|
||
s_socks[sockfd].in_use = 0;
|
||
s_socks[sockfd].state = NET_SOCK_STATE_CLOSED;
|
||
s_ws[sockfd] = INVALID_SOCKET;
|
||
|
||
s_net_errno = NET_OK;
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:主动拆掉当前 TCP 会话、保留监听(与 MCU 端同名 API——E-02 收口)
|
||
* 入口参数:sockfd - socket 描述符
|
||
* 出口参数:无
|
||
* 返回值:0 成功(含幂等:无活动会话),-1 失败
|
||
* 限定条件:socket 已打开(in_use == 1)
|
||
* 函数说明:模拟器为多连接后端(conn != 监听 fd):等价关闭该数据连接的
|
||
* Winsock 句柄并收口 CLOSED(fd 槽不释放,外层 net_close 收尾);
|
||
* 监听 fd 不受影响。无活动会话幂等返 0。
|
||
*/
|
||
int net_drop_conn(int sockfd) {
|
||
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS) {
|
||
s_net_errno = NET_ERR_BADF;
|
||
return -1;
|
||
}
|
||
if (s_socks[sockfd].in_use == 0) {
|
||
s_net_errno = NET_ERR_BADF;
|
||
return -1;
|
||
}
|
||
if (s_socks[sockfd].state != NET_SOCK_STATE_ESTABLISHED &&
|
||
s_socks[sockfd].state != NET_SOCK_STATE_CONNECTING) {
|
||
/* 无活动会话:幂等成功 */
|
||
s_net_errno = NET_OK;
|
||
return 0;
|
||
}
|
||
|
||
closesocket(s_ws[sockfd]);
|
||
s_ws[sockfd] = INVALID_SOCKET;
|
||
s_socks[sockfd].state = NET_SOCK_STATE_CLOSED;
|
||
|
||
s_net_errno = NET_OK;
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:注册事件回调
|
||
* 入口参数:sockfd - socket 描述符
|
||
* cb - 回调函数(NULL 取消注册)
|
||
* arg - 用户参数
|
||
* 出口参数:无
|
||
* 返回值:0 成功,-1 失败
|
||
* 限定条件:socket 已创建
|
||
* 函数说明:模拟器以非阻塞 I/O 直接感知事件,回调仅存储不主动触发
|
||
*/
|
||
int net_set_event_cb(int sockfd, net_event_cb_t cb, void *arg) {
|
||
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS || s_socks[sockfd].in_use == 0) {
|
||
s_net_errno = NET_ERR_BADF;
|
||
return -1;
|
||
}
|
||
|
||
s_socks[sockfd].event_cb = cb;
|
||
s_socks[sockfd].event_arg = arg;
|
||
|
||
s_net_errno = NET_OK;
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:设置 Socket 阻塞/非阻塞**模式属性**(与 MCU 端同名 API 对齐)
|
||
* 入口参数:sockfd - socket 描述符
|
||
* on - 1 = 阻塞(net_socket/net_accept 默认);0 = 非阻塞
|
||
* 返回值:0 成功;-1 失败(描述符无效/未占用)
|
||
* 限定条件:socket 已创建
|
||
* 函数说明:Winsock 句柄恒非阻塞(FIONBIO,UI 主循环安全底线),本属性只驱动
|
||
* 包装层的等待策略(net_recv 无限轮询 / 立即 WOULDBLOCK),
|
||
* 语义与 MCU 端"模式挂控制块属性"一致
|
||
*/
|
||
int net_setblocking(int sockfd, int on) {
|
||
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS || s_socks[sockfd].in_use == 0) {
|
||
s_net_errno = NET_ERR_BADF;
|
||
return -1;
|
||
}
|
||
|
||
s_socks[sockfd].blocking = (on != 0) ? 1U : 0U;
|
||
|
||
s_net_errno = NET_OK;
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:获取 socket 最后错误码
|
||
* 入口参数:无
|
||
* 返回值:最近一次 net_* 操作设置的错误码(NET_OK / NET_ERR_xxx)
|
||
* 限定条件:无
|
||
*/
|
||
int net_get_errno(void) {
|
||
return s_net_errno;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:将点分十进制 IP 字符串转为网络字节序 32 位地址
|
||
* 入口参数:cp - 点分十进制字符串(如 "192.168.1.100")
|
||
* 出口参数:无
|
||
* 返回值:网络字节序 IP(参数无效返回 0)
|
||
* 限定条件:无
|
||
* 函数说明:与 MCU 端 net_inet_addr **逐字同语义**(2026-10-24:两套 IP 字节序约定统一为 POSIX 网络序):
|
||
* 按「第一段在主机序最高字节」累加,再经 NET_HTONL 转网络序,结果满足
|
||
* POSIX/Winsock 的 in_addr 不变量——&s_addr 的第 0 个内存字节 == 第一段。
|
||
* 本模拟器的 to_ws_addr()/from_ws_addr() 是把 net_sockaddr_in **整块 memcpy**
|
||
* 进 sockaddr_in 的,所以只有两边同族时才成立:旧实现(小端累加后再换端)
|
||
* 交给 Winsock 的是反序地址,只有从不走 inet_addr 的路径(bind ANY、
|
||
* getpeername)才没暴露。改完后本函数与 net_inet_ntoa 互逆、与 Winsock 直拷兼容。
|
||
* 段值超 255 按低 8 位截断;失败返回 0(不引入 NET_INADDR_NONE 判定)。
|
||
*/
|
||
uint32_t net_inet_addr(const char *cp) {
|
||
uint32_t addr = 0;
|
||
int i;
|
||
|
||
if (cp == NULL) {
|
||
return 0;
|
||
}
|
||
|
||
for (i = 0; i < 4; i++) {
|
||
uint32_t val = 0;
|
||
while (*cp >= '0' && *cp <= '9') {
|
||
val = val * 10 + (uint32_t)(*cp - '0');
|
||
cp++;
|
||
}
|
||
/* 高位先入:第 0 段左移三次后落在最高字节 */
|
||
addr = (addr << 8) | (val & 0xFFU);
|
||
if (*cp == '.') {
|
||
cp++;
|
||
}
|
||
}
|
||
|
||
return NET_HTONL(addr);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:将网络字节序 32 位 IP 转为点分十进制字符串
|
||
* 入口参数:addr - 网络字节序 IP
|
||
* buf - 输出缓冲区(至少 16 字节)
|
||
* 出口参数:buf - 填充字符串
|
||
* 返回值:buf 指针(buf 为 NULL 时返回 NULL)
|
||
* 限定条件:buf 非空且长度足够
|
||
* 函数说明:NTOHL 后主机序数值的高字节是点分第一段(与 MCU 端同步修复,
|
||
* E13 定案:原低段在前显示成字节反转)
|
||
*/
|
||
char *net_inet_ntoa(uint32_t addr, char *buf) {
|
||
uint32_t naddr = NET_NTOHL(addr);
|
||
|
||
if (buf == NULL) {
|
||
return NULL;
|
||
}
|
||
|
||
snprintf(buf, 16, "%lu.%lu.%lu.%lu",
|
||
(unsigned long)((naddr >> 24) & 0xFF),
|
||
(unsigned long)((naddr >> 16) & 0xFF),
|
||
(unsigned long)((naddr >> 8) & 0xFF),
|
||
(unsigned long)(naddr & 0xFF));
|
||
|
||
return buf;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:端口字节序转换(主机序 -> 网络序)
|
||
* 入口参数:hostshort - 主机序端口
|
||
* 返回值:网络序端口
|
||
* 限定条件:无
|
||
*/
|
||
uint16_t net_htons(uint16_t hostshort) {
|
||
return NET_HTONS(hostshort);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:端口字节序转换(网络序 -> 主机序)
|
||
* 入口参数:netshort - 网络序端口
|
||
* 返回值:主机序端口
|
||
* 限定条件:无
|
||
*/
|
||
uint16_t net_ntohs(uint16_t netshort) {
|
||
return NET_NTOHS(netshort);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:IP 地址字节序转换(主机序 -> 网络序)
|
||
* 入口参数:hostlong - 主机序 IP
|
||
* 返回值:网络序 IP
|
||
* 限定条件:无
|
||
*/
|
||
uint32_t net_htonl(uint32_t hostlong) {
|
||
return NET_HTONL(hostlong);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:IP 地址字节序转换(网络序 -> 主机序)
|
||
* 入口参数:netlong - 网络序 IP
|
||
* 返回值:主机序 IP
|
||
* 限定条件:无
|
||
*/
|
||
uint32_t net_ntohl(uint32_t netlong) {
|
||
return NET_NTOHL(netlong);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:获取 Socket 控制块指针
|
||
* 入口参数:sockfd - socket 描述符
|
||
* 返回值:控制块指针;描述符越界或已释放(in_use == 0)时返回 NULL
|
||
* 限定条件:无
|
||
* 函数说明:上层据此判断"监听 Socket 是否随客户端一同失效"(单连接模型)
|
||
*/
|
||
net_sock_t *net_get_sock(int sockfd) {
|
||
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS) {
|
||
return NULL;
|
||
}
|
||
if (s_socks[sockfd].in_use == 0) {
|
||
return NULL;
|
||
}
|
||
return &s_socks[sockfd];
|
||
}
|
||
|
||
/*
|
||
* 函数功能:获取 Socket 本地地址(IP + Port)
|
||
* 入口参数:sockfd - socket 描述符
|
||
* addr - 输出:本地地址
|
||
* addrlen - 输入输出:地址结构体长度
|
||
* 出口参数:addr/addrlen - 填充
|
||
* 返回值:0 成功,-1 失败
|
||
* 限定条件:socket 已创建
|
||
* 函数说明:经 Winsock getsockname 读取实际绑定地址
|
||
*/
|
||
int net_getsockname(int sockfd, struct net_sockaddr *addr, int *addrlen) {
|
||
struct sockaddr_in wsaddr;
|
||
int wslen;
|
||
|
||
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS || s_socks[sockfd].in_use == 0) {
|
||
s_net_errno = NET_ERR_BADF;
|
||
return -1;
|
||
}
|
||
if (addr == NULL || addrlen == NULL ||
|
||
*addrlen < (int)sizeof(struct net_sockaddr_in)) {
|
||
s_net_errno = NET_ERR_INVAL;
|
||
return -1;
|
||
}
|
||
|
||
wslen = sizeof(wsaddr);
|
||
if (getsockname(s_ws[sockfd], (struct sockaddr *)&wsaddr, &wslen) == SOCKET_ERROR) {
|
||
s_net_errno = NET_ERR;
|
||
return -1;
|
||
}
|
||
|
||
from_ws_addr(&wsaddr, addr);
|
||
*addrlen = (int)sizeof(struct net_sockaddr_in);
|
||
|
||
s_net_errno = NET_OK;
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:获取 Socket 对端地址(IP + Port)(与 MCU 端同名 API——E13 定案)
|
||
* 入口参数:sockfd - socket 描述符
|
||
* addr - 输出:对端地址
|
||
* addrlen - 输入输出:地址结构体长度
|
||
* 出口参数:addr/addrlen - 填充
|
||
* 返回值:0 成功,-1 失败(未连接 NOTCONN)
|
||
* 限定条件:socket 已连接
|
||
* 函数说明:经 Winsock getpeername 读取;模拟器多连接后端下 conn fd 有真实对端
|
||
*/
|
||
int net_getpeername(int sockfd, struct net_sockaddr *addr, int *addrlen) {
|
||
struct sockaddr_in wsaddr;
|
||
int wslen;
|
||
|
||
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS || s_socks[sockfd].in_use == 0) {
|
||
s_net_errno = NET_ERR_BADF;
|
||
return -1;
|
||
}
|
||
if (addr == NULL || addrlen == NULL ||
|
||
*addrlen < (int)sizeof(struct net_sockaddr_in)) {
|
||
s_net_errno = NET_ERR_INVAL;
|
||
return -1;
|
||
}
|
||
|
||
wslen = sizeof(wsaddr);
|
||
if (getpeername(s_ws[sockfd], (struct sockaddr *)&wsaddr, &wslen) == SOCKET_ERROR) {
|
||
s_net_errno = NET_ERR_NOTCONN;
|
||
return -1;
|
||
}
|
||
|
||
from_ws_addr(&wsaddr, addr);
|
||
*addrlen = (int)sizeof(struct net_sockaddr_in);
|
||
|
||
s_net_errno = NET_OK;
|
||
return 0;
|
||
}
|