# eduwin 网络模块 API

> 头文件：`eduwin_net.h`（或通过 `eduwin.h` 统一引用）

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

## 初始化 / 清理

```c
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` 的返回值）。

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

## DNS 解析

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

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

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

## TCP

```c
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_timeout` 的 `timeout_ms` 传 `-1` 表示无限等待。`tcp_peer_addr` 返回格式 `"ip:port"`。

```c
// 时间服务器查询示例
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

```c
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_recv` 的 `src_ip` 和 `src_port` 可传 NULL 跳过读取。

## HTTP

```c
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_type` 和 `auth_header` 可传 NULL 跳过。

```c
// 获取公网 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 编码

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

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

## WebSocket Hub

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

### 设计思路

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

### 创建 / 关闭

```c
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://`）。

### 连接管理

```c
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` — 断开指定连接

### 订阅 / 发布

```c
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 |

### 示例

```c
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 模板：

```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
```
