195 lines
5.2 KiB
C
195 lines
5.2 KiB
C
/**
|
||
* @file qdx_port_win32.c
|
||
* @brief Windows (WinSock2 + WinAPI) 平台移植层
|
||
*
|
||
* 目的:将 qdx_port.h 声明的 HAL 接口映射到 Windows API,
|
||
* 使 QDXnetworkStack 库能够在 PC 端运行。
|
||
*
|
||
* 注意:本文件仅用于 PC 端 Demo 模拟,不适用于 MCU 移植。
|
||
*/
|
||
|
||
#include "qdx_port.h"
|
||
|
||
#include <process.h> /* _beginthreadex */
|
||
#include <stddef.h>
|
||
#include <stdio.h>
|
||
#include <windows.h>
|
||
#include <winsock2.h>
|
||
|
||
|
||
/* ============================================================
|
||
* 时间与延迟
|
||
* ============================================================ */
|
||
|
||
/* 获取系统启动以来的毫秒数 */
|
||
uint32_t qdx_port_get_tick_ms(void) { return (uint32_t)GetTickCount(); }
|
||
|
||
/* 阻塞延迟指定毫秒 */
|
||
void qdx_port_delay_ms(uint32_t ms) { Sleep(ms); }
|
||
|
||
/* ============================================================
|
||
* TCP 网络操作 (基于 WinSock2)
|
||
* ============================================================ */
|
||
|
||
/**
|
||
* @brief 创建 TCP 套接字并连接到远端
|
||
*
|
||
* 连接成功后设置 500ms 接收超时,防止接收线程永久阻塞。
|
||
*/
|
||
qdx_socket_t qdx_port_tcp_connect(const char *ip, uint16_t port) {
|
||
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||
if (sock == INVALID_SOCKET) {
|
||
return NULL;
|
||
}
|
||
|
||
struct sockaddr_in addr;
|
||
addr.sin_family = AF_INET;
|
||
addr.sin_port = htons(port);
|
||
addr.sin_addr.s_addr = inet_addr(ip);
|
||
|
||
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR) {
|
||
closesocket(sock);
|
||
return NULL;
|
||
}
|
||
|
||
/* 设置接收超时 500ms,使 recv 线程可定期检查连接状态 */
|
||
DWORD timeout_ms = 500;
|
||
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout_ms,
|
||
sizeof(timeout_ms));
|
||
|
||
printf("[Port] TCP 已连接: %s:%d\n", ip, port);
|
||
|
||
/* 将 SOCKET 转为不透明指针(SOCKET 本身是 unsigned int 类型) */
|
||
return (qdx_socket_t)(intptr_t)sock;
|
||
}
|
||
|
||
/* 关闭 TCP 套接字 */
|
||
void qdx_port_tcp_close(qdx_socket_t sock) {
|
||
if (sock) {
|
||
closesocket((SOCKET)(intptr_t)sock);
|
||
}
|
||
}
|
||
|
||
/* TCP 发送 */
|
||
int32_t qdx_port_tcp_send(qdx_socket_t sock, const uint8_t *data,
|
||
uint32_t len) {
|
||
if (!sock)
|
||
return -1;
|
||
int ret = send((SOCKET)(intptr_t)sock, (const char *)data, (int)len, 0);
|
||
if (ret == SOCKET_ERROR) {
|
||
return -1;
|
||
}
|
||
return (int32_t)ret;
|
||
}
|
||
|
||
/**
|
||
* @brief TCP 接收(非阻塞/短超时)
|
||
*
|
||
* 返回值约定:
|
||
* > 0 : 实际收到的字节数
|
||
* = 0 : 超时无数据
|
||
* < 0 : 连接断开或错误
|
||
*/
|
||
int32_t qdx_port_tcp_recv(qdx_socket_t sock, uint8_t *buf, uint32_t max_len) {
|
||
if (!sock)
|
||
return -1;
|
||
int ret = recv((SOCKET)(intptr_t)sock, (char *)buf, (int)max_len, 0);
|
||
if (ret == SOCKET_ERROR) {
|
||
int err = WSAGetLastError();
|
||
/* WSAETIMEDOUT / WSAEWOULDBLOCK 表示超时,视为无数据可读 */
|
||
if (err == WSAETIMEDOUT || err == WSAEWOULDBLOCK) {
|
||
return 0;
|
||
}
|
||
return -1; /* 真实错误 */
|
||
}
|
||
if (ret == 0) {
|
||
return -1; /* 服务端主动关闭连接 */
|
||
}
|
||
return (int32_t)ret;
|
||
}
|
||
|
||
/* ============================================================
|
||
* 互斥锁 (基于 CRITICAL_SECTION)
|
||
* ============================================================ */
|
||
|
||
/* 创建互斥锁 */
|
||
qdx_mutex_t qdx_port_mutex_create(void) {
|
||
CRITICAL_SECTION *cs = (CRITICAL_SECTION *)malloc(sizeof(CRITICAL_SECTION));
|
||
if (!cs)
|
||
return NULL;
|
||
InitializeCriticalSection(cs);
|
||
return (qdx_mutex_t)cs;
|
||
}
|
||
|
||
/* 加锁 */
|
||
void qdx_port_mutex_lock(qdx_mutex_t mutex) {
|
||
if (mutex) {
|
||
EnterCriticalSection((CRITICAL_SECTION *)mutex);
|
||
}
|
||
}
|
||
|
||
/* 解锁 */
|
||
void qdx_port_mutex_unlock(qdx_mutex_t mutex) {
|
||
if (mutex) {
|
||
LeaveCriticalSection((CRITICAL_SECTION *)mutex);
|
||
}
|
||
}
|
||
|
||
/* 销毁互斥锁 */
|
||
void qdx_port_mutex_delete(qdx_mutex_t mutex) {
|
||
if (mutex) {
|
||
DeleteCriticalSection((CRITICAL_SECTION *)mutex);
|
||
free(mutex);
|
||
}
|
||
}
|
||
|
||
/* ============================================================
|
||
* 线程 (基于 _beginthreadex)
|
||
* ============================================================ */
|
||
|
||
/**
|
||
* @brief 线程启动包装器
|
||
*
|
||
* 目的:_beginthreadex 要求 unsigned __stdcall 签名,
|
||
* 而 qdx_thread_entry_t 是 void(*)(void*),
|
||
* 需要通过此包装器做签名适配。
|
||
*/
|
||
typedef struct {
|
||
qdx_thread_entry_t entry;
|
||
void *arg;
|
||
} ThreadWrapper_t;
|
||
|
||
static unsigned __stdcall thread_wrapper(void *param) {
|
||
ThreadWrapper_t *tw = (ThreadWrapper_t *)param;
|
||
qdx_thread_entry_t entry = tw->entry;
|
||
void *arg = tw->arg;
|
||
free(tw); /* 释放包装结构,生命周期结束 */
|
||
entry(arg);
|
||
return 0;
|
||
}
|
||
|
||
/* 创建后台线程 */
|
||
int8_t qdx_port_thread_create(const char *name, qdx_thread_entry_t entry,
|
||
void *arg, uint32_t stack_size,
|
||
uint8_t priority) {
|
||
(void)name; /* Windows 线程不直接支持命名 */
|
||
(void)priority; /* 简化 Demo 不设线程优先级 */
|
||
|
||
ThreadWrapper_t *tw = (ThreadWrapper_t *)malloc(sizeof(ThreadWrapper_t));
|
||
if (!tw)
|
||
return -1;
|
||
tw->entry = entry;
|
||
tw->arg = arg;
|
||
|
||
HANDLE h =
|
||
(HANDLE)_beginthreadex(NULL, stack_size, thread_wrapper, tw, 0, NULL);
|
||
if (h == NULL) {
|
||
free(tw);
|
||
return -1;
|
||
}
|
||
|
||
/* 分离线程句柄,后台自动运行 */
|
||
CloseHandle(h);
|
||
return 0;
|
||
}
|