df7947c5007fb142e031083e2ce301a082b8831e
OmniSocket
OmniSocket 当前包含 3 个核心程序:
omni_peeromni_hubomni_bridge
三者统一支持 tcp | udp | kcp 三种传输协议。
构建
本地构建:
make
生成文件:
build/omni_peerbuild/omni_hubbuild/omni_bridge
Jetson 场景常用 ARM64 交叉编译:
make arm64
生成文件:
build/arm64/omni_peerbuild/arm64/omni_hubbuild/arm64/omni_bridge
程序说明
omni_peer
omni_peer 支持两种工作模式:
hub模式:连接hub或bridgedirect模式:peer之间直接互连
常见用途:
- 发送文本命令
- 发送文件
- 接收文件
omni_hub
omni_hub 是中心注册与转发节点,通常部署在公网服务器或中心网络位置。
主要职责:
- 维护
client_id -> session - 转发
peer之间的bind / tunnel / status
omni_bridge
omni_bridge 是桥接节点,用于将远端 peer 接入上游 hub。
主要职责:
- 上游连接
hub - 下游监听本地入口供
peer接入 - 适用于
A <-> C <-> D <-> B这类桥接链路
当前限制:
- 一个
bridge仅支持一个下游peer - 下游
peer的-i必须与bridge -i保持一致 - 上下游必须使用同一种协议,不支持协议转换
- 当前实现更接近“单下游、单身份桥接”,而不是通用多租户中继
角色说明
A:本地电脑B:JetsonC:公网 Hub 服务器D:公网 Bridge 服务器
下面示例中的 <proto> 可替换为 tcp、udp 或 kcp。
说明:
- 以下
omni_peer示例统一采用长连接交互模式 - 启动后连接会保持,不再使用“传输一次后自动退出”的一次性写法
- 文本和文件传输通过终端中的交互命令完成
- 若启动命令中使用了
-m或-F,程序会执行启动动作模式,而不是进入当前 README 使用的交互模式
常用参数与命令
| 分类 | 写法 | 含义 |
|---|---|---|
| 启动参数 | -i <client_id> |
当前 peer 的逻辑身份。Hub 会根据这个 ID 记录 client_id -> session 映射,例如 -i pc 表示“我是 pc”,-i jetson 表示“我是 jetson”。 |
| 启动参数 | -b <peer_id> |
启动后默认绑定的目标 peer。例如 -b jetson 表示后续直接输入 send ... 或 put ... 时,默认发给 jetson。 |
| 启动参数 | -d <peer_id> |
启动动作模式下的显式目标。通常与 -m 或 -F 配合使用,表示把启动时的那条消息或那个文件直接发给指定目标。 |
| 启动参数 | -o <output_file> |
本地接收文件时的落盘路径。收到文件后会写入当前机器上的这个路径,例如 -o /tmp/from_pc.bin。 |
| 交互命令 | bind <peer_id> |
将默认目标切换到指定 peer,后续 send 或 put 默认发给它。 |
| 交互命令 | send <text> |
向当前默认目标发送一条文本消息。 |
| 交互命令 | say <peer_id> <text> |
向指定 peer 发送一条文本消息,不修改当前默认目标。 |
| 交互命令 | put <file> |
将当前机器上的文件发送给当前默认目标。 |
| 交互命令 | push <peer_id> <file> |
将当前机器上的文件发送给指定 peer,不修改当前默认目标。 |
| 交互命令 | show |
显示当前本地状态,例如 client_id、当前绑定目标和输出路径。 |
| 交互命令 | quit |
退出当前 omni_peer 进程。 |
场景 1:点对点直传
A <-> B
B 端监听:
./build/omni_peer -M direct -p <proto> -L 9001 -i jetson -o /tmp/from_pc.bin
A 端连接:
./build/omni_peer -M direct -p <proto> -H <B_IP> -P 9001 -i pc -b jetson -o /tmp/from_jetson.bin
连接建立后,可在 A 端输入:
send start
put /tmp/input.bin
如需反向 B -> A,可在 B 端输入:
put /path/to/file.bin
场景 2:通过 Hub 中转
A <-> C <-> B
C 端启动 Hub:
- C 维护着一张
client_id -> session映射表,用于记录谁是pc、谁是jetson,并据此转发bind / tunnel / status
./build/omni_hub -p <proto> -P 9002
B 端连接 Hub:
./build/omni_peer -p <proto> -H <C_IP> -P 9002 -i jetson -o /tmp/from_pc.bin
A 端连接 Hub:
./build/omni_peer -p <proto> -H <C_IP> -P 9002 -i pc -b jetson -o /tmp/from_jetson.bin
连接建立后,可在 A 端输入:
send start
put /tmp/input.bin
如需反向 B -> A,可在 B 端输入:
bind pc
put /path/to/file.bin
场景 3:通过 Bridge 桥接
A <-> C <-> D <-> B
C 端启动 Hub:
- C 仍然维护
client_id -> session映射表;A 以pc注册到 C,Bridge 以jetson这个逻辑身份注册到 C
./build/omni_hub -p <proto> -P 9003
D 端启动 Bridge:
./build/omni_bridge -p <proto> -H <C_IP> -P 9003 -i jetson -L 9004
B 端连接 Bridge:
./build/omni_peer -p <proto> -H <D_IP> -P 9004 -i jetson -o /tmp/from_pc.bin
A 端连接 Hub:
./build/omni_peer -p <proto> -H <C_IP> -P 9003 -i pc -b jetson -o /tmp/from_jetson.bin
连接建立后,可在 A 端输入:
send start
put /tmp/input.bin
如需反向 B -> A,可在 B 端输入:
bind pc
put /path/to/file.bin
说明:
- 该场景已经实现
A -> C -> D -> B与B -> D -> C -> A的桥接转发 - 但当前
bridge仍是单下游、单身份模型,不是完整的多节点桥接网络
日志与指标
输出位置
- 终端文本日志:默认输出到
stderr,格式为key=value - 结构化日志:默认追加到当前工作目录下的
omni_logs.jsonl - 性能快照统一写在
component="perf"的 JSONL 记录里 - 终端里会额外出现
component=perf_udp_loss文本行;同一批 UDP 丢包字段已经合并写进对应的component="perf"JSONL 行 - 周期性性能快照大约每
1s打一次;进程退出时会再打一条tag="final"
当前文档口径
下面这些说法要以当前实现为准,不要再按旧文档理解:
processing / queue / transmission / propagation / end_to_end都是“当前实现下的本地观测值或估算值”,不是严格意义上的物理链路精确测量值。processing_*当前表示本地应用层处理耗时,主要来自文件分片封装、接收端写盘等路径;它不是“某一块硬件 CPU 的完整开销画像”。queue_*和transmission_*是基于最近活跃窗口的吞吐和缓冲/队列状态反推出来的估算值。最近没有足够流量样本、样本太小或者当前速率太低时,这两个值可能直接为0。propagation_*当前来自min_rtt_ms / 2的估算;如果当前协议没有 RTT 样本,这组字段就是0。end_to_end_*当前只在“最终接收文件的 peer”上有值,来源是发送端分片里的origin_ts_ms。发送文件前会先做一次时钟同步,把发送端时间对齐到接收端时钟域;如果同步没建立,这组字段会保持0,而不是给出误导性的跨机结果。- UDP 丢包统计只在
UDP 文件接收侧 peer上有值;UDP 发送侧、Hub、Bridge 不会产出这组汇总。 udp_retrans字段当前还没有实现应用层 UDP 重传统计,所以现在始终是0。TCP/KCP当前记录的是重传次数/重传字节/累计发送分片,不直接记录“重传频率”这个单独字段。send_buffer_pct_*/recv_buffer_pct_*是占用率风格的指标,但不保证永远严格落在0-100;尤其 KCP 等待队列超过窗口时,理论上可以大于100。send_call_* / recv_call_* / proto_* / processing_*有时会是0,常见原因不是没统计,而是当前时间分辨率是毫秒,很多本地操作小于1ms。
基础与吞吐字段
| 文档含义 | JSONL 字段 | 单位 | 当前语义 | 什么时候有值 / 为什么会是 0 |
|---|---|---|---|---|
| 时间戳 | ts_ms |
ms | 这条日志写出的单调时间戳 | 始终有值 |
| 日志分类 | level / component / tag |
- | component="perf" 表示性能快照,tag 常见为 peer_transport_send、peer_transport_recv、final |
始终有值 |
| 身份上下文 | app / proto / mode / role / self_id |
- | 程序名、协议、模式、角色、逻辑 ID | hub 的 self_id 为空是正常的 |
| 运行时长 | elapsed_ms |
ms | 当前进程从启动到本次快照的时长 | 始终有值 |
| 累计发送字节 | bytes_sent |
bytes | 当前进程累计发送的协议帧总字节 | 无发送时为 0 |
| 累计接收字节 | bytes_recv |
bytes | 当前进程累计接收的协议帧总字节 | 无接收时为 0 |
| 发送次数 | send_count |
count | 当前进程累计发送帧次数 | 无发送时为 0 |
| 接收次数 | recv_count |
count | 当前进程累计接收帧次数 | 无接收时为 0 |
| 瞬时发送带宽 | tx_current_mbps |
Mbps | 最近一个统计窗口内的发送速率 | 当前窗口没流量时为 0 |
| 瞬时接收带宽 | rx_current_mbps |
Mbps | 最近一个统计窗口内的接收速率 | 当前窗口没流量时为 0 |
| 平均发送带宽 | tx_avg_mbps |
Mbps | 进程启动到当前的平均发送速率 | 从未发送时为 0 |
| 平均接收带宽 | rx_avg_mbps |
Mbps | 进程启动到当前的平均接收速率 | 从未接收时为 0 |
| 传输进度字节 | progress_bytes |
bytes | 当前文件传输已完成字节数 | 没有文件传输时为 0 |
| 总工作量 | total_work_bytes |
bytes | 当前文件总大小 | 没有文件传输时为 0 |
| 传输进度百分比 | progress_pct |
% | progress_bytes / total_work_bytes * 100 |
没有文件传输时为 0 |
调用耗时与延迟字段
| 文档含义 | JSONL 字段 | 单位 | 当前语义 | 什么时候有值 / 为什么会是 0 |
|---|---|---|---|---|
| 应用层发送调用 | send_call_last_ms / send_call_min_ms / send_call_max_ms / send_call_avg_ms |
ms | peer_transport_send() 调用耗时 |
没有发送,或每次发送都小于 1ms 时可能为 0 |
| 应用层接收调用 | recv_call_last_ms / recv_call_min_ms / recv_call_max_ms / recv_call_avg_ms |
ms | peer_transport_next_event() 接收调用耗时 |
没有接收,或每次接收都小于 1ms 时可能为 0 |
| 协议层发送耗时 | proto_send_avg_ms |
ms | TCP/UDP/KCP 实际 send 路径耗时 EWMA | 发送很快且小于 1ms 时常为 0 |
| 协议层接收耗时 | proto_recv_avg_ms |
ms | TCP/UDP/KCP 实际 recv 路径耗时 EWMA | 接收很快且小于 1ms 时常为 0 |
| 本地处理耗时 | processing_avg_ms / processing_min_ms / processing_max_ms |
ms | 当前进程内的分片封装、写盘等本地处理耗时 | 仅在文件发送/接收路径上采样;操作太快时可能为 0 |
| 排队延迟估算 | queue_avg_ms / queue_min_ms / queue_max_ms |
ms | 根据发送/接收队列字节数和最近活跃窗口速率估算 | 没有足够活跃样本时为 0;小样本/低速率时可能偏保守 |
| 传输延迟估算 | transmission_avg_ms / transmission_min_ms / transmission_max_ms |
ms | 根据最近活跃窗口速率估算“这些字节推上链路需要多久” | 没有足够活跃样本时为 0;小样本/低速率时可能偏保守 |
| 传播延迟估算 | propagation_avg_ms / propagation_min_ms / propagation_max_ms |
ms | 基于 min_rtt_ms / 2 的估算 |
当前协议没有 RTT 样本时为 0 |
| 端到端延迟 | end_to_end_avg_ms / end_to_end_min_ms / end_to_end_max_ms |
ms | 发送端分片 origin_ts_ms 对齐到接收端时钟后,到接收端处理完成时刻的差值 |
只在最终接收文件的 peer 上有值;若时钟同步未建立则为 0 |
可靠性字段
| 文档含义 | JSONL 字段 | 单位 | 当前语义 | 什么时候有值 / 为什么会是 0 |
|---|---|---|---|---|
| TCP 重传次数 | tcp_retrans |
count | 内核 TCP_INFO 的累计重传次数 |
仅 TCP 有值;未重传或非 TCP 时为 0 |
| TCP 数据段数 | tcp_data_segs_out |
count | TCP 累计发送数据段数 | 仅 TCP 有值;非 TCP 时为 0 |
| TCP 发送字节 | tcp_data_bytes_sent |
bytes | TCP 累计发送数据字节 | 仅 TCP 有值;非 TCP 时为 0 |
| TCP 重传字节 | tcp_retrans_bytes |
bytes | TCP 累计重传字节 | 仅 TCP 有值;未重传或非 TCP 时为 0 |
| UDP 重传次数 | udp_retrans |
count | 预留字段,当前未实现 | 当前始终为 0 |
| KCP 重传次数 | kcp_retrans |
count | KCP 内部累计重传分片数 | 仅 KCP 有值;未重传或非 KCP 时为 0 |
| KCP 数据分片数 | kcp_data_segs_out |
count | KCP 累计发送分片数 | 仅 KCP 有值;非 KCP 时为 0 |
| KCP 发送字节 | kcp_data_bytes_sent |
bytes | KCP 累计发送分片字节 | 仅 KCP 有值;非 KCP 时为 0 |
| KCP 重传字节 | kcp_retrans_bytes |
bytes | KCP 累计重传字节 | 仅 KCP 有值;未重传或非 KCP 时为 0 |
| UDP 预期分片数 | udp_expected_chunks |
count | UDP 文件接收端预期应收到的总分片数 | 仅 UDP 文件接收侧 peer 有值;其他角色为 0 |
| UDP 实收分片数 | udp_received_chunks |
count | UDP 文件接收端实际收到的唯一分片数 | 仅 UDP 文件接收侧 peer 有值;其他角色为 0 |
| UDP 丢失分片数 | udp_lost_chunks |
count | 根据 seq 推断的缺失分片数 |
完整收到时为 0;非 UDP 接收侧也为 0 |
| UDP 丢包率 | udp_loss_rate_pct |
% | udp_lost_chunks / udp_expected_chunks * 100 |
完整收到时为 0;非 UDP 接收侧也为 0 |
| UDP 连续丢包区间数 | udp_loss_burst_count |
count | 丢包区间个数 | 无丢包时为 0 |
| UDP 最大连续丢包长度 | udp_loss_burst_max_len |
count | 单个丢包区间的最大长度 | 无丢包时为 0 |
| UDP 丢包区间摘要 | udp_loss_ranges |
csv string | 例如 4-6,9,12-13 |
无丢包时为空串 |
| UDP 丢失序号样本 | udp_loss_seq_sample |
csv string | 最多记录一部分缺失 seq 样本 |
无丢包时为空串 |
| UDP 接收窗口分布 | udp_recv_window_dist |
csv string | window_id:count,例如 0:3,1:28 |
仅 UDP 接收侧有值;没有样本时为空串 |
资源与算法字段
| 文档含义 | JSONL 字段 | 单位 | 当前语义 | 什么时候有值 / 为什么会是 0 |
|---|---|---|---|---|
| 发送缓冲区占用 | send_buffer_pct_last / send_buffer_pct_avg / send_buffer_pct_max |
% 风格值 | Socket 或 KCP 发送队列占用率样本 | 没有缓冲采样时为 0;KCP 拥塞时可能大于 100 |
| 接收缓冲区占用 | recv_buffer_pct_last / recv_buffer_pct_avg / recv_buffer_pct_max |
% 风格值 | Socket 或 KCP 接收队列占用率样本 | 没有缓冲采样时为 0 |
| 拥塞窗口 | cwnd_last / cwnd_avg / cwnd_max |
协议窗口大小 | TCP/KCP 当前拥塞窗口样本 | UDP 没有拥塞窗口,因此为 0 |
| RTT | last_rtt_ms / min_rtt_ms / max_rtt_ms |
ms | TCP TCP_INFO 或 KCP rx_srtt 的 RTT 样本 |
UDP 当前没有 RTT 探测,因此为 0 |
实测样本
以下值来自本仓库当前实现的本地回环测试,仅用于说明“字段已经能落到 JSONL 且当前名字是什么”,不是固定性能指标。
| 样本 | 关键字段 | 实测值 |
|---|---|---|
TCP 点对点直传发送端 |
progress_pct / cwnd_last / last_rtt_ms / tcp_data_bytes_sent |
100 / 10 / 1 / 287984 |
UDP Hub 中转接收端 |
progress_pct / udp_expected_chunks / udp_received_chunks / udp_lost_chunks / udp_recv_window_dist |
100 / 3 / 3 / 0 / 0:3 |
KCP Bridge 桥接节点 |
cwnd_last / last_rtt_ms / kcp_data_bytes_sent / queue_avg_ms |
2 / 7 / 265 / 33991.342083 |
KCP 最终接收端 |
progress_pct / end_to_end_avg_ms / cwnd_last |
100 / 121.333333 / 2 |
为什么有些字段“存在但没有值”
| 情况 | 典型字段 | 说明 |
|---|---|---|
| 协议不适用 | tcp_* 出现在 UDP/KCP;kcp_* 出现在 TCP/UDP;cwnd_* 出现在 UDP |
字段统一保留,便于同一份 JSONL 脚本处理;不适用的协议就写 0 |
| 还没实现 | udp_retrans |
现在没有应用层 UDP 重传,所以这个字段只是预留 |
| 当前角色不产出 | udp_expected_chunks、udp_loss_*、end_to_end_* |
UDP 丢包统计只在最终接收文件的 UDP peer 上有值;end_to_end_* 也主要只在最终接收端有值 |
| 没有采样到 RTT | last_rtt_ms、propagation_* |
UDP 当前没有 RTT 探针;TCP/KCP 只有在拿到对应协议样本后才有值 |
| 没有建立时钟同步 | end_to_end_* |
发送端尚未和接收端完成 TIME_SYNC_* 探测,或同步结果太旧/未到达 |
| 时间分辨率太粗 | send_call_*、proto_send_avg_ms、processing_* |
当前很多路径按毫秒计时,本地回环下大量操作小于 1ms,所以会显示 0 |
| 当前没有任务 | progress_* |
只注册但没有文件传输时,这组字段自然是 0 |
Description
OmniSocket 是一款基于纯 C 语言开发的高性能网络传输工具库。项目深度集成 TCP、UDP 及 KCP 协议栈,专为嵌入式(树莓派 ARM)与服务器(Linux x86)跨平台通信设计
Languages
C
98.7%
Makefile
0.9%
Shell
0.4%