466 lines
21 KiB
Markdown
466 lines
21 KiB
Markdown
OmniSocketGo -> OmniSocketC 转换计划
|
||
|
||
Context
|
||
|
||
将现有的 Go 语言实现的 UDP/KCP 传输层项目 (OmniSocketGo) 转换为纯 C 语言项目,运行在 Linux 系统上。
|
||
|
||
原项目架构:A(Jetson) <-> C(relay cloud) <-> D(hub cloud) <-> B(host)
|
||
- B <-> D:KCP 链路
|
||
- D <-> C:UDP relay 转发
|
||
- C <-> A:KCP 链路(A 通过 relay C 连接到 hub D)
|
||
- 最终目的:B 和 A 之间双向传输数据
|
||
|
||
转换要求:
|
||
- 只保留 UDP 和 KCP,不需要 TCP
|
||
- 不需要写测试
|
||
- 完整实现协议层、传输层、日志事件系统
|
||
- Linux only
|
||
|
||
项目位置
|
||
|
||
OmniSocketGo/c/ — 作为当前 Go 项目的子目录
|
||
|
||
项目结构
|
||
|
||
c/
|
||
├── Makefile
|
||
├── README.md
|
||
├── include/
|
||
│ ├── protocol.h # 协议消息定义 + 编解码
|
||
│ ├── transport_kcp.h # KCP 连接封装
|
||
│ ├── transport_udp.h # UDP 连接封装(含 Linux timestamping)
|
||
│ ├── linux_timestamping.h # Linux SO_TIMESTAMPING 底层实现
|
||
│ ├── kcp_packet_debug.h # KCP packet-level kernel timestamp debug logger
|
||
│ ├── kcp_session_stats.h # KCP session stats (RTO/SRTT) logger
|
||
│ ├── tx_timestamp_debug.h # TX errqueue timestamp debug logger
|
||
│ ├── server_kcp_hub.h # KCP Hub (D 节点)
|
||
│ ├── server_udp_relay.h # UDP Relay (C 节点)
|
||
│ ├── peer_kcp_client.h # KCP Peer Client (A/B 节点)
|
||
│ ├── latencylog.h # 延迟日志事件系统
|
||
│ ├── interactive.h # 交互式命令行
|
||
│ └── cJSON.h # JSON 库 (第三方轻量级)
|
||
├── src/
|
||
│ ├── protocol.c
|
||
│ ├── transport_kcp.c
|
||
│ ├── transport_udp.c
|
||
│ ├── linux_timestamping.c
|
||
│ ├── kcp_packet_debug.c
|
||
│ ├── kcp_session_stats.c
|
||
│ ├── tx_timestamp_debug.c
|
||
│ ├── server_kcp_hub.c
|
||
│ ├── server_udp_relay.c
|
||
│ ├── peer_kcp_client.c
|
||
│ ├── latencylog.c
|
||
│ ├── interactive.c
|
||
│ └── cJSON.c
|
||
├── cmd/
|
||
│ ├── kcpserver.c # 主程序: KCP Hub 或 UDP Relay
|
||
│ ├── kcppeer.c # 主程序: KCP Peer (A/B)
|
||
│ └── kcpping.c # 主程序: KCP Ping 工具
|
||
└── third_party/
|
||
└── kcp/
|
||
├── ikcp.h # KCP 协议核心实现 (github.com/skywind3000/kcp)
|
||
└── ikcp.c
|
||
|
||
依赖说明
|
||
|
||
- KCP: 使用 skywind3000/kcp 的原始 C 实现 (ikcp.h/ikcp.c),替代 Go 的 xtaci/kcp-go/v5
|
||
- JSON: 使用 cJSON (DaveGamble/cJSON) 替代 Go 的 encoding/json
|
||
- 线程: 使用 pthread 替代 Go goroutine
|
||
- 同步: 使用 pthread_mutex/pthread_rwlock 替代 Go sync.Mutex/sync.RWMutex
|
||
|
||
模块实现计划
|
||
|
||
1. 第三方库集成
|
||
|
||
- 下载 ikcp.h/ikcp.c (skywind3000/kcp)
|
||
- 下载 cJSON.h/cJSON.c (DaveGamble/cJSON)
|
||
|
||
2. protocol.h / protocol.c
|
||
|
||
对应 Go: cmd/internal/protocol/message.go + codec.go
|
||
|
||
// 消息类型
|
||
typedef enum {
|
||
MSG_TYPE_TEXT = 0,
|
||
MSG_TYPE_FILE = 1,
|
||
MSG_TYPE_REGISTER = 2,
|
||
MSG_TYPE_ERROR = 3,
|
||
} message_type_t;
|
||
|
||
// 消息结构
|
||
typedef struct {
|
||
message_type_t type;
|
||
uint64_t id;
|
||
char from[64];
|
||
char to[64];
|
||
char file_name[256];
|
||
uint8_t *body;
|
||
int body_len;
|
||
} message_t;
|
||
|
||
#define MAX_FRAME_SIZE (8 * 1024 * 1024)
|
||
#define SERVER_PEER_ID "server"
|
||
|
||
核心函数:
|
||
- int protocol_encode_message(const message_t *msg, uint8_t **out, int *out_len) — 编码消息为 [4B headerLen][header JSON][body]
|
||
- int protocol_decode_message(const uint8_t *data, int data_len, message_t *msg) — 解码
|
||
- int protocol_write_frame(int fd, const uint8_t *payload, int payload_len) — 写带长度前缀的帧 (用于 KCP stream)
|
||
- int protocol_read_frame(int fd, uint8_t **payload, int *payload_len) — 读帧
|
||
- int protocol_write_message(int fd, const message_t *msg) — 完整编码+写帧
|
||
- int protocol_read_message(int fd, message_t *msg) — 读帧+解码
|
||
- int protocol_validate_message(const message_t *msg) — 校验
|
||
- void message_free(message_t *msg) — 释放 body 内存
|
||
|
||
注意: KCP session 在 stream 模式下行为类似 TCP,需要 [4B frameLen] 前缀来分帧。
|
||
|
||
3. latencylog.h / latencylog.c
|
||
|
||
对应 Go: cmd/internal/latencylog/logger.go
|
||
|
||
// 事件名常量
|
||
#define EVENT_A_APP_PREP_BEGIN "A_APP_PREP_BEGIN"
|
||
#define EVENT_SEND_HANDOFF_BEGIN "send_handoff_begin"
|
||
#define EVENT_SEND_HANDOFF_END "send_handoff_end"
|
||
#define EVENT_B_APP_RECV "B_APP_RECV"
|
||
#define EVENT_B_PERSIST_BEGIN "B_PERSIST_BEGIN"
|
||
#define EVENT_B_PERSIST_END "B_PERSIST_END"
|
||
// ... 其他事件
|
||
|
||
typedef struct {
|
||
int64_t ts_unix_nano;
|
||
char node_role[16];
|
||
char node_id[64];
|
||
char event[32];
|
||
message_type_t message_type;
|
||
uint64_t message_id;
|
||
char from[64];
|
||
char to[64];
|
||
char file_name[256];
|
||
int body_size;
|
||
} latency_event_t;
|
||
|
||
typedef struct latency_logger latency_logger_t;
|
||
|
||
核心函数:
|
||
- latency_logger_t *latencylog_new_jsonl(const char *path) — 创建 JSONL 文件日志器
|
||
- void latencylog_log_event(latency_logger_t *logger, const latency_event_t *event) — 写事件
|
||
- void latencylog_log_message_event(latency_logger_t *logger, const char *node_role, const char *node_id, const char *event_name, const message_t
|
||
*msg) — 为业务消息记事件
|
||
- void latencylog_close(latency_logger_t *logger) — 关闭
|
||
- int latencylog_is_business_message(const message_t *msg) — 判断是否业务消息
|
||
|
||
4. transport_kcp.h / transport_kcp.c
|
||
|
||
对应 Go: cmd/internal/transport/kcp.go + kcp_packet_conn.go
|
||
|
||
KCP 连接封装,底层用 raw ikcp + UDP socket:
|
||
|
||
typedef struct kcp_conn {
|
||
ikcpcb *kcp;
|
||
int udp_fd;
|
||
struct sockaddr_in remote_addr;
|
||
pthread_mutex_t write_mu;
|
||
pthread_t recv_thread; // 底层 UDP -> ikcp_input 的线程
|
||
latency_logger_t *logger;
|
||
char node_role[16];
|
||
char node_id[64];
|
||
int closed;
|
||
} kcp_conn_t;
|
||
|
||
核心函数:
|
||
- kcp_conn_t *kcp_conn_dial(const char *server_addr, const char *bind_ip, const char *bind_device) — 客户端拨号
|
||
- kcp_conn_t *kcp_conn_accept(int udp_fd, struct sockaddr_in *remote, uint32_t conv) — 服务端接受
|
||
- int kcp_conn_send(kcp_conn_t *conn, const message_t *msg) — 发送消息
|
||
- int kcp_conn_receive(kcp_conn_t *conn, message_t *msg) — 接收消息
|
||
- void kcp_conn_close(kcp_conn_t *conn) — 关闭
|
||
|
||
KCP 配置参数(与 Go 版一致):
|
||
#define KCP_NODELAY 1
|
||
#define KCP_INTERVAL 10
|
||
#define KCP_RESEND 2
|
||
#define KCP_NC 1
|
||
#define KCP_WND_SIZE 256
|
||
#define KCP_MTU 1400
|
||
|
||
KCP 底层架构说明:
|
||
Go 版使用 kcp-go 库,该库内部维护了一个 Listener 来多路复用一个 UDP socket 上的多个 KCP session(通过 conv ID 区分)。在 C 中需要自行实现:
|
||
- 服务端:一个 UDP socket 监听,一个接收线程读取所有 UDP 包,根据 conv ID 分发到对应的 ikcpcb
|
||
- 客户端:一个 UDP socket,一个 ikcpcb,一个后台线程负责 UDP recv -> ikcp_input
|
||
|
||
5. transport_udp.h / transport_udp.c
|
||
|
||
对应 Go: cmd/internal/transport/udp.go + udp_linux.go
|
||
|
||
typedef struct udp_conn {
|
||
int fd;
|
||
struct sockaddr_in peer_addr;
|
||
syscall_rawconn_t raw; // syscall.RawConn 等价
|
||
int linux_timestamping_enabled;
|
||
latency_logger_t *logger;
|
||
tx_timestamp_debug_logger_t *tx_debug_logger;
|
||
uint32_t tx_packet_seq;
|
||
// pending TX records for errqueue correlation
|
||
struct udp_tx_pending *pending_tx;
|
||
char node_role[16];
|
||
char node_id[64];
|
||
pthread_mutex_t write_mu;
|
||
} udp_conn_t;
|
||
|
||
完整实现 Linux SO_TIMESTAMPING:
|
||
- TX: SOF_TIMESTAMPING_TX_SCHED + SOF_TIMESTAMPING_TX_SOFTWARE + OPT_ID
|
||
- RX: SOF_TIMESTAMPING_RX_SOFTWARE
|
||
- errqueue 采集: recvmsg(MSG_ERRQUEUE) 读取 SCM_TIMESTAMPING 控制消息
|
||
- TX timestamp debug logger: 记录 send_chunk / errqueue_event 到 JSONL
|
||
- 对应 Go 文件: udp_linux.go, tx_timestamp_debug.go
|
||
|
||
同时为 KCP packet conn 实现类似的 timestamping:
|
||
- 对应 Go 文件: kcp_packet_conn_linux.go, kcp_packet_debug.go
|
||
- KCP 底层 UDP 包的 TX/RX kernel timestamp 记录
|
||
|
||
KCP session stats 完整实现:
|
||
- session-level: conv, RTO, SRTT, SRTTVar 周期采样
|
||
- 对应 Go 文件: kcp_session_stats.go
|
||
|
||
6. server_kcp_hub.h / server_kcp_hub.c
|
||
|
||
对应 Go: cmd/internal/server/kcp_hub.go
|
||
|
||
typedef struct {
|
||
pthread_rwlock_t lock;
|
||
// peer_id -> kcp_conn_t* 的哈希表
|
||
struct peer_entry *peers; // 简单链表或哈希表
|
||
int peer_count;
|
||
latency_logger_t *logger;
|
||
// relay 相关
|
||
int relay_udp_fd;
|
||
struct sockaddr_in relay_peer_addr;
|
||
int relay_peer_known;
|
||
} kcp_hub_t;
|
||
|
||
核心函数:
|
||
- kcp_hub_t *kcp_hub_new(latency_logger_t *logger) — 创建 hub
|
||
- int kcp_hub_serve_session(kcp_hub_t *hub, kcp_conn_t *conn) — 处理新会话(注册 + 转发循环)
|
||
- void kcp_hub_set_relay(kcp_hub_t *hub, int udp_fd, struct sockaddr_in *peer_addr) — 配置 relay
|
||
- int kcp_hub_serve_relay(kcp_hub_t *hub) — relay 接收循环
|
||
- void kcp_hub_free(kcp_hub_t *hub) — 释放
|
||
|
||
服务端 KCP listener 实现:
|
||
- 主 UDP socket 监听
|
||
- 收到新 conv ID 时创建新 ikcpcb
|
||
- 用 pthread 为每个 session 创建处理线程
|
||
|
||
7. server_udp_relay.h / server_udp_relay.c
|
||
|
||
对应 Go: cmd/internal/server/udp_relay.go
|
||
|
||
typedef struct {
|
||
int downstream_fd; // 监听端
|
||
int upstream_fd; // 连接到 hub D 的 UDP
|
||
struct sockaddr_in upstream_addr;
|
||
struct sockaddr_in client_addr;
|
||
int client_known;
|
||
pthread_mutex_t lock;
|
||
} udp_relay_t;
|
||
|
||
核心函数:
|
||
- udp_relay_t *udp_relay_new(int listen_fd, struct sockaddr_in *upstream_addr) — 创建
|
||
- int udp_relay_serve(udp_relay_t *relay) — 双向转发循环(两个线程)
|
||
- void udp_relay_close(udp_relay_t *relay) — 关闭
|
||
|
||
8. peer_kcp_client.h / peer_kcp_client.c
|
||
|
||
对应 Go: cmd/internal/peer/kcp_client.go + persist.go
|
||
|
||
typedef struct {
|
||
char id[64];
|
||
kcp_conn_t *conn;
|
||
latency_logger_t *logger;
|
||
uint64_t next_msg_id; // atomic
|
||
pthread_mutex_t id_mu;
|
||
} kcp_client_t;
|
||
|
||
核心函数:
|
||
- kcp_client_t *kcp_client_dial(const char *server_addr, const char *peer_id, ...) — 连接并注册
|
||
- int kcp_client_send_text(kcp_client_t *c, const char *to, const char *text) — 发文本
|
||
- int kcp_client_send_file(kcp_client_t *c, const char *to, const char *path) — 发文件
|
||
- int kcp_client_receive(kcp_client_t *c, message_t *msg) — 接收
|
||
- int kcp_client_persist_message(kcp_client_t *c, const message_t *msg, const char *inbox_dir) — 持久化
|
||
- void kcp_client_close(kcp_client_t *c) — 关闭
|
||
|
||
9. interactive.h / interactive.c
|
||
|
||
对应 Go: cmd/kcppeer/interactive.go
|
||
|
||
交互式命令行 REPL:
|
||
- help / text <peer> <message> / file <peer> <path> / quit
|
||
- int run_interactive_shell(kcp_client_t *client) — 运行交互循环
|
||
|
||
10. cmd/kcpserver.c
|
||
|
||
对应 Go: cmd/kcpserver/main.go
|
||
|
||
用法:
|
||
kcpserver -listen 0.0.0.0:10909 # hub 模式
|
||
kcpserver -mode relay -listen 0.0.0.0:10909 -relay-remote 172.21.32.15:10909 # relay 模式
|
||
|
||
- 解析命令行参数 (getopt)
|
||
- hub 模式:创建 KCP listener -> 接受连接 -> kcp_hub_serve_session
|
||
- relay 模式:创建 UDP relay -> udp_relay_serve
|
||
|
||
11. cmd/kcppeer.c
|
||
|
||
对应 Go: cmd/kcppeer/main.go
|
||
|
||
用法:
|
||
kcppeer -id peer-a -server 172.21.32.15:10909 -relay-via 106.55.173.235:10909 -inbox-dir inbox/a
|
||
kcppeer -id peer-b -server 81.70.156.140:10909 -inbox-dir inbox/b
|
||
|
||
- 连接到 KCP server
|
||
- 启动接收线程
|
||
- 运行交互式 shell 或单次发送
|
||
|
||
12. cmd/kcpping.c
|
||
|
||
对应 Go: cmd/kcpping/main.go + platform_linux.go
|
||
|
||
KCP ping 工具:
|
||
- ping 模式: 发 JSON payload, 计算 RTT
|
||
- echo 模式: 回弹文本消息
|
||
- 统计: min/avg/max/p50/p95/p99/stddev
|
||
|
||
KCP session 多路复用实现(核心难点)
|
||
|
||
Go 版的 kcp-go 库在一个 UDP socket 上透明地多路复用多个 KCP session。C 版需要手动实现:
|
||
|
||
typedef struct kcp_listener {
|
||
int udp_fd;
|
||
pthread_t recv_thread;
|
||
pthread_mutex_t sessions_lock;
|
||
// conv -> kcp_session 的哈希表
|
||
struct kcp_session_entry *sessions;
|
||
// 新会话通知队列
|
||
kcp_conn_t **accept_queue;
|
||
int accept_queue_head, accept_queue_tail, accept_queue_cap;
|
||
pthread_mutex_t accept_lock;
|
||
pthread_cond_t accept_cond;
|
||
} kcp_listener_t;
|
||
|
||
- kcp_listener_t *kcp_listen(const char *addr, const char *bind_device) — 创建 listener
|
||
- kcp_conn_t *kcp_accept(kcp_listener_t *listener) — 阻塞等待新会话
|
||
- 内部 recv_thread 循环读 UDP 包,解析前 4 字节 conv ID,分发到对应 ikcpcb
|
||
- 未知 conv ID 时创建新 session 并放入 accept_queue
|
||
|
||
编译
|
||
|
||
CC = gcc
|
||
CFLAGS = -Wall -Wextra -O2 -pthread -D_GNU_SOURCE
|
||
LDFLAGS = -lpthread
|
||
|
||
SRCS = src/protocol.c src/transport_kcp.c src/transport_udp.c \
|
||
src/server_kcp_hub.c src/server_udp_relay.c \
|
||
src/peer_kcp_client.c src/latencylog.c src/interactive.c \
|
||
src/cJSON.c third_party/kcp/ikcp.c
|
||
|
||
all: kcpserver kcppeer kcpping
|
||
|
||
kcpserver: cmd/kcpserver.c $(SRCS)
|
||
$(CC) $(CFLAGS) -Iinclude -Ithird_party/kcp -o $@ $^ $(LDFLAGS
|
||
|
||
kcppeer: cmd/kcppeer.c $(SRCS)
|
||
$(CC) $(CFLAGS) -Iinclude -Ithird_party/kcp -o $@ $^ $(LDFLAGS
|
||
|
||
kcpping: cmd/kcpping.c $(SRCS)
|
||
$(CC) $(CFLAGS) -Iinclude -Ithird_party/kcp -o $@ $^ $(LDFLAGS
|
||
|
||
验证方法
|
||
|
||
1. 编译: make all 无错误无警告
|
||
2. 单机测试:
|
||
- 启动 hub: ./kcpserver -listen 0.0.0.0:10909
|
||
- 启动 peer-a: ./kcppeer -id peer-a -server 127.0.0.1:10909 -inbox-dir inbox/a
|
||
- 启动 peer-b: ./kcppeer -id peer-b -server 127.0.0.1:10909 -inbox-dir inbox/b
|
||
- peer-b shell 中: text peer-a hello
|
||
- 验证 peer-a 收到消息并落盘到 inbox/a/
|
||
3. 跨机器 relay 测试:
|
||
- D 机器: ./kcpserver -listen 0.0.0.0:10909
|
||
- C 机器: ./kcpserver -mode relay -listen 0.0.0.0:10909 -relay-remote <D_IP>:10909
|
||
- A 机器: ./kcppeer -id peer-a -server <D_IP>:10909 -relay-via <C_IP>:10909 -inbox-dir inbox/a
|
||
- B 机器: ./kcppeer -id peer-b -server <D_IP>:10909 -inbox-dir inbox/b
|
||
4. kcpping 测试:
|
||
- echo 端: ./kcpping -id peer-a -server <IP>:10909 -echo
|
||
- ping 端: ./kcpping -id peer-b -server <IP>:10909 -to peer-a -count 20 -interval 100
|
||
|
||
实现顺序
|
||
|
||
1. 集成第三方库 (ikcp, cJSON)
|
||
2. protocol 模块 (消息编解码)
|
||
3. latencylog 模块 (日志事件)
|
||
4. transport_kcp 模块 (KCP 连接 + listener 多路复用)
|
||
5. transport_udp 模块 (UDP 连接,简化 timestamping)
|
||
6. server_udp_relay 模块 (C 节点 relay)
|
||
7. server_kcp_hub 模块 (D 节点 hub)
|
||
8. peer_kcp_client 模块 (A/B 节点 peer + persist)
|
||
9. interactive 模块 (交互 shell)
|
||
10. cmd/kcpserver.c 主程序
|
||
11. cmd/kcppeer.c 主程序
|
||
12. cmd/kcpping.c 主程序
|
||
13. Makefile + README
|
||
14. 编译测试
|
||
|
||
简化决策
|
||
|
||
- 不实现 TCP 传输: 去除 transport/tcp.go, server/hub.go(TCP版), peer/client.go(TCP版) 等 TCP 相关代码
|
||
- 不写测试: 去除所有 _test.go 对应的测试代码
|
||
- 完整实现 Linux timestamping: 完整移植 SO_TIMESTAMPING 的 TX/RX timestamp 采集,包括 errqueue TX sched/software timestamp 和 RX software
|
||
timestamp,以及对应的 debug logger (KCPPacketDebugLogger, TXTimestampDebugLogger)
|
||
- 完整实现 KCP session stats: 包括 session-level RTO/SRTT 采样和 JSONL 记录
|
||
- 不实现 latency summary/chart: 不实现 latencysummary 工具和 HTML chart 生成(这是离线分析工具,不属于核心传输功能)
|
||
- peer 哈希表: 使用简单链表实现,hub 连接数不多时性能足够
|
||
|
||
|
||
# OmniSocketGo -> OmniSocketC 全量 UDP/KCP 迁移计划
|
||
|
||
## Summary
|
||
- 在仓库新增 `c/` 子项目,作为 Linux-only、C11、`make` 驱动的独立实现;现有 Go 项目保留不动,作为行为对照。
|
||
- 迁移范围按“全量 Go 对齐,但去掉 TCP 和离线 summary/chart”执行:保留 UDP/KCP 协议、纯 UDP 程序族、KCP 程序族、运行时 JSONL 日志、Linux timestamping、KCP packet debug、KCP session stats、以及 KCP hub-to-hub 内部 relay 能力。
|
||
- 你当前草案需要修正的关键点有 5 个:`protocol_*frame(int fd, ...)` 不适合 KCP;KCP 必须补齐 `ikcp_update/check` 调度与 conv 多路复用;纯 UDP 程序族不能省略;`latencysummary`/HTML chart 本次不迁移;Makefile 需要修正链接目标并统一输出到 `c/bin/`。
|
||
|
||
## Public Interfaces
|
||
- 新增二进制:`kcpserver`、`kcppeer`、`kcpping`、`udpserver`、`udppeer`、`udpping`、`udprelay`。
|
||
- `kcpserver` 保留当前 Go 旗标语义:`-mode=hub|relay`、`-listen`、`-bind-device`、`-relay-remote`、deprecated relay aliases、`-latency-log`、`-kcp-ts-debug-log`、`-kcp-session-stats-log`、`-kcp-session-stats-interval`。
|
||
- `kcppeer` 保留当前 Go 旗标语义:`-id`、`-server`、`-relay-via`、`-to`、`-text`、`-file`、`-bind-ip`、`-bind-device`、`-inbox-dir`、`-interactive`、`-latency-log`、`-kcp-ts-debug-log`、`-kcp-session-stats-log`、`-kcp-session-stats-interval`。
|
||
- `kcpping`、`udpserver`、`udppeer`、`udpping`、`udprelay` 的参数与输出行为对齐当前 Go 入口;`udpserver` 默认不开 Linux timestamping,只有设置 `-tx-ts-debug-log` 时才启用。
|
||
- 协议层改为内存接口,不再设计 fd 风格 API:`message_t`、datagram 编解码、stream frame 编解码、增量 frame feed。
|
||
- 运行时日志层保留当前 JSON 字段和事件名;server/hub 继续作为 black-box relay,不新增端到端业务事件。
|
||
- 内部网络 API 包括:`udp_conn_t`、`kcp_conn_t`、`kcp_listener_t`、`udp_hub_t`、`kcp_hub_t`、`udp_relay_t`、`udp_client_t`、`kcp_client_t`;KCP hub-to-hub relay 只做库级能力,不新增额外 CLI。
|
||
|
||
## Implementation Changes
|
||
- 目录固定为 `c/include`、`c/src`、`c/cmd`、`c/third_party/{ikcp,cjson}`、`c/bin`、`c/README.md`、`c/Makefile`。
|
||
- 第三方依赖直接 vendoring 到仓库:`ikcp` 用于 KCP 核心,`cJSON` 同时用于协议头、ping payload、运行时日志。
|
||
- 协议规则完全保留:`text/file/register/error`、`ServerPeerID`、`8 MiB` 限制、UTF-8 校验、`file_name` 约束、`register/error` 来源与目标约束。
|
||
- 线上 wire format 完全保留:UDP datagram 为 `[4B headerLen][header JSON][body]`;KCP stream 为 `[4B frameLen][4B headerLen][header JSON][body]`。
|
||
- inbox 持久化完全保留:文本追加写 `messages.log` JSONL;文件落盘为 `<from>-<messageID>-<baseFileName>`。
|
||
- UDP 传输层实现 connected/unconnected 两种发送模式,保留 register/forward 消息收发、Linux SO_TIMESTAMPING、TX errqueue 关联、JSONL debug 记录。
|
||
- KCP 客户端连接采用“一连接一 UDP socket + 一 `ikcpcb` + 一接收线程 + 一 update 线程 + 一阻塞接收缓冲区/条件变量”模型。
|
||
- KCP 服务端监听采用“单 listener UDP socket + 单 listener RX 线程 + conv->session 表 + accept 队列”模型;每个 session 拥有自己的 `ikcpcb`、update 线程、接收缓冲区和关闭状态,发送通过 listener 共享 socket 和写锁完成。
|
||
- `kcpserver` 的 relay 模式保持为原始 UDP 端口转发,不解码协议;`udprelay` 同样保持透明字节转发。
|
||
- 纯 UDP hub、KCP hub、双 peer、双 ping 工具、两套 interactive shell 全部对齐现有 Go 行为。
|
||
- KCP hub 保留“先本地投递,再尝试 relay”的策略;未知目标、重复注册、已注册 peer 再发 `register/error`、过大 relay 消息等错误路径全部保留。
|
||
- Linux 观测能力完整迁移:业务事件 JSONL、UDP TX debug、KCP packet debug、KCP session/process stats;不迁移 `latencysummary` 与 HTML chart。
|
||
|
||
## Acceptance
|
||
- 在 Linux 上执行 `make` 能无缺失符号地构建 7 个二进制,并输出到 `c/bin/`。
|
||
- 纯 UDP 冒烟通过:`udpserver` + 两个 `udppeer` 可双向收发文本和文件,`udpping` 的 echo/ping 正常。
|
||
- 单 hub KCP 冒烟通过:`kcpserver` + 两个 `kcppeer` 可双向收发文本和文件,`kcpping` 的 echo/ping 正常。
|
||
- README 目标拓扑通过:D 跑 `kcpserver -mode=hub`,C 跑 `kcpserver -mode=relay`,A 用 `-relay-via C` 连 D,B 直连 D,A/B 双向传输正常。
|
||
- 全量 Go 对齐场景通过:两个 KCP hub 通过内部 raw UDP relay API 互通,跨 hub 文本、文件、错误回送行为与当前 Go 一致。
|
||
- 负路径通过:重复注册被拒、未注册 UDP sender 被拒、未知目标返回 `error`、已注册 peer 发送 `register/error` 被拒、oversize relayed message 在实际 `WriteTo` 前被拒、`bind-ip`/`bind-device` 非法值在启动时失败。
|
||
- 打开任一日志旗标后,生成的 JSONL 记录字段名、事件名、时间戳语义与现有运行时日志一致,并在 Linux 支持的情况下出现非零 kernel timestamps。
|
||
|
||
## Assumptions
|
||
- 默认编译器为 `gcc`/`clang`,编译参数基线为 `-std=c11 -Wall -Wextra -O2 -pthread -D_GNU_SOURCE`。
|
||
- 本次不迁移任何 Go 测试文件,也不为 C 版编写自动化测试;验证仅靠 Linux 构建和手工场景回归。
|
||
- 本次不迁移 TCP 入口,也不迁移 `latencysummary`/HTML chart。
|
||
- hub-to-hub relay 在 C 版中实现为内部库能力,保持与当前 Go 仓库一致的范围,不额外扩展新的公共命令。
|