eduwin 网络模块 API

头文件:eduwin_net.h(或通过 eduwin.h 统一引用)

基于 Winsock2 + WinHTTP,零外部依赖。提供 TCP、UDP、HTTP、DNS 四层能力。

初始化 / 清理

int  net_init(void);
void net_cleanup(void);
const char* net_error(void);
void net_free(void *ptr);

所有网络操作前必须先调 net_init(),退出时调 net_cleanup()net_error() 返回最近一次操作的错误描述(仅调试用,值不会 auto-clear)。net_free() 释放模块内部分配的内存(如 tcp_peer_addr 的返回值)。

net_init();
// ... 网络操作 ...
net_cleanup();

DNS 解析

int dns_lookup(const char *hostname, char *ip, int ip_size);

将域名解析为 IPv4 地址字符串。返回 0 成功,ip 中存储点分十进制 IP(如 "202.202.0.100")。

char ip[64];
if (dns_lookup("www.xtu.edu.cn", ip, sizeof(ip)) == 0)
    printf("IP: %s", ip);  // 输出: IP: 202.202.0.100

TCP

typedef struct tcp_s tcp_t;

// 客户端连接
tcp_t* tcp_connect(const char *host, int port);

// 服务端监听
tcp_t* tcp_listen(int port);
tcp_t* tcp_accept(tcp_t *server);

// 收发
int  tcp_send(tcp_t *conn, const char *data, int len);
int  tcp_recv(tcp_t *conn, char *buf, int buf_size);
int  tcp_recv_timeout(tcp_t *conn, char *buf, int buf_size, int timeout_ms);

// 工具
char* tcp_peer_addr(tcp_t *conn);  // 需 net_free
void  tcp_close(tcp_t *conn);

tcp_recv_timeouttimeout_ms-1 表示无限等待。tcp_peer_addr 返回格式 "ip:port"

// 时间服务器查询示例
tcp_t *conn = tcp_connect("time.nist.gov", 13);
if (conn) {
    char buf[1024] = {0};
    int n = tcp_recv_timeout(conn, buf, sizeof(buf)-1, 5000);
    if (n > 0) printf("时间: %s", buf);
    tcp_close(conn);
}

UDP

typedef struct udp_s udp_t;

udp_t* udp_create(int port);          // port=0 自动分配
int  udp_send(udp_t *sock, const char *host, int port,
                     const char *data, int len);
int  udp_recv(udp_t *sock, char *buf, int buf_size,
                     char *src_ip, int *src_port, int timeout_ms);
void udp_close(udp_t *sock);

udp_create(0) 创建绑定到随机端口的 UDP 套接字。udp_recvsrc_ipsrc_port 可传 NULL 跳过读取。

HTTP

typedef struct http_s http_t;

http_t* http_get(const char *url);
http_t* http_post(const char *url, const char *body,
                                 const char *content_type,
                                 const char *auth_header);
int  http_status(http_t *resp);
const char* http_body(http_t *resp);
int  http_body_len(http_t *resp);
void http_free(http_t *resp);

支持 HTTPS(通过 WinHTTP)。content_typeauth_header 可传 NULL 跳过。

// 获取公网 IP
http_t *resp = http_get("https://httpbin.org/ip");
if (resp) {
    printf("HTTP %d: %s", http_status(resp), http_body(resp));
    http_free(resp);
}

URL 编码

char* url_encode(const char *str);  // 需 net_free

对字符串进行 URL 百分号编码。适用于构造自定义 HTTP 查询参数。

WebSocket Hub

基于原生 Winsock WebSocket 协议(RFC 6455)实现的轻量级 pub/sub 消息枢纽。单线程、无锁、非阻塞轮询。

设计思路

  • 服务端监听端口,接受 WS 客户端连接
  • 客户端通过 JSON 指令订阅/取消订阅话题
  • 消息通过话题路由,发送者不会收到自己的消息
  • 所有客户端对等,不分角色

创建 / 关闭

typedef struct ws_hub_s ws_hub_t;

ws_hub_t*  ws_hub_create(int port);
void       ws_hub_close(ws_hub_t *hub);

ws_hub_create 在指定端口启动 WS 服务器(ws://)。

连接管理

int  ws_hub_accept(ws_hub_t *hub, int timeout_ms);
int  ws_hub_count(ws_hub_t *hub);
int  ws_hub_conn_id(ws_hub_t *hub, int index);
void ws_hub_disconnect(ws_hub_t *hub, int conn_id);
  • ws_hub_accept — 接受新连接,返回 conn_id(-1=超时/错误)
  • ws_hub_count — 当前连接数
  • ws_hub_conn_id — 按索引获取 conn_id
  • ws_hub_disconnect — 断开指定连接

订阅 / 发布

int  ws_hub_subscribe(ws_hub_t *hub, int conn_id, const char *topic);
int  ws_hub_unsubscribe(ws_hub_t *hub, int conn_id, const char *topic);
int  ws_hub_publish(ws_hub_t *hub, int sender_id, const char *topic,
                    const char *data, int len);
int  ws_hub_recv(ws_hub_t *hub, int conn_id,
                 char *buf, int buf_size, int timeout_ms);
int  ws_hub_poll(ws_hub_t *hub, int timeout_ms);
  • ws_hub_subscribe — 订阅话题(一个连接可订阅多个)
  • ws_hub_unsubscribe — 取消订阅
  • ws_hub_publish — 向某话题所有订阅者(排除发送者)推送消息
  • ws_hub_recv — 接收指定连接的一条消息
  • ws_hub_poll — 轮询所有连接,返回有消息待读的 conn_id(-1=无)

限制

上限
最大连接数64
话题名长度64 字符
每连接可订阅话题数16
单条消息最大长度16 KB

示例

ws_hub_t *hub = ws_hub_create(9099);
if (!hub) { /* 错误处理 */ }

while (g_running) {
    /* 接受新连接 */
    int cid = ws_hub_accept(hub, 200);
    if (cid >= 0) {
        ws_hub_subscribe(hub, cid, "agent");
        ws_hub_subscribe(hub, cid, "js");
    }

    /* 轮询消息 */
    int ready = ws_hub_poll(hub, 200);
    if (ready < 0) continue;

    char buf[16384];
    int len = ws_hub_recv(hub, ready, buf, sizeof(buf) - 1, 0);
    if (len <= 0) { ws_hub_disconnect(hub, ready); continue; }
    buf[len] = '\0';

    /* 解析 JSON,按 topic 发布 */
    // ws_hub_publish(hub, ready, topic, buf, len);
}

ws_hub_close(hub);

完整示例

参见 samples/network_info_demo/samples/ai_browser_demo/

编译要求

链接时需要额外两个库:

ws2_32.lib winhttp.lib

对应的 build.bat 模板:

cl main.c /Fe:demo.exe /link eduwin.lib user32.lib gdi32.lib gdiplus.lib comctl32.lib comdlg32.lib shell32.lib winmm.lib ws2_32.lib winhttp.lib