# OmniSocket OmniSocket 当前包含 3 个核心程序: - `omni_peer` - `omni_hub` - `omni_bridge` 三者统一支持 `tcp | udp | kcp` 三种传输协议。 ## 构建 本地构建: ```bash make ``` 生成文件: - `build/omni_peer` - `build/omni_hub` - `build/omni_bridge` Jetson 场景常用 ARM64 交叉编译: ```bash make arm64 ``` 生成文件: - `build/arm64/omni_peer` - `build/arm64/omni_hub` - `build/arm64/omni_bridge` ## 程序说明 ### `omni_peer` `omni_peer` 支持两种工作模式: - `hub` 模式:连接 `hub` 或 `bridge` - `direct` 模式:`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`:Jetson - `C`:公网 Hub 服务器 - `D`:公网 Bridge 服务器 下面示例中的 `` 可替换为 `tcp`、`udp` 或 `kcp`。 说明: - 以下 `omni_peer` 示例统一采用长连接交互模式 - 启动后连接会保持,不再使用“传输一次后自动退出”的一次性写法 - 文本和文件传输通过终端中的交互命令完成 - 若启动命令中使用了 `-m` 或 `-F`,程序会执行启动动作模式,而不是进入当前 README 使用的交互模式 ## 常用参数与命令 | 分类 | 写法 | 含义 | | --- | --- | --- | | 启动参数 | `-i ` | 当前 `peer` 的逻辑身份。Hub 会根据这个 ID 记录 `client_id -> session` 映射,例如 `-i pc` 表示“我是 pc”,`-i jetson` 表示“我是 jetson”。 | | 启动参数 | `-b ` | 启动后默认绑定的目标 `peer`。例如 `-b jetson` 表示后续直接输入 `send ...` 或 `put ...` 时,默认发给 `jetson`。 | | 启动参数 | `-d ` | 启动动作模式下的显式目标。通常与 `-m` 或 `-F` 配合使用,表示把启动时的那条消息或那个文件直接发给指定目标。 | | 启动参数 | `-o ` | 本地接收文件时的落盘路径。收到文件后会写入当前机器上的这个路径,例如 `-o /tmp/from_pc.bin`。 | | 交互命令 | `bind ` | 将默认目标切换到指定 `peer`,后续 `send` 或 `put` 默认发给它。 | | 交互命令 | `send ` | 向当前默认目标发送一条文本消息。 | | 交互命令 | `say ` | 向指定 `peer` 发送一条文本消息,不修改当前默认目标。 | | 交互命令 | `put ` | 将当前机器上的文件发送给当前默认目标。 | | 交互命令 | `push ` | 将当前机器上的文件发送给指定 `peer`,不修改当前默认目标。 | | 交互命令 | `show` | 显示当前本地状态,例如 `client_id`、当前绑定目标和输出路径。 | | 交互命令 | `quit` | 退出当前 `omni_peer` 进程。 | ## 场景 1:点对点直传 `A <-> B` B 端监听: ```bash ./build/omni_peer -M direct -p -L 9001 -i jetson -o /tmp/from_pc.bin ``` A 端连接: ```bash ./build/omni_peer -M direct -p -H -P 9001 -i pc -b jetson -o /tmp/from_jetson.bin ``` 连接建立后,可在 A 端输入: ```text send start put /tmp/input.bin ``` 如需反向 `B -> A`,可在 B 端输入: ```text put /path/to/file.bin ``` ## 场景 2:通过 Hub 中转 `A <-> C <-> B` C 端启动 Hub: - C 维护着一张 `client_id -> session` 映射表,用于记录谁是 `pc`、谁是 `jetson`,并据此转发 `bind / tunnel / status` ```bash ./build/omni_hub -p -P 9002 ``` B 端连接 Hub: ```bash ./build/omni_peer -p -H -P 9002 -i jetson -o /tmp/from_pc.bin ``` A 端连接 Hub: ```bash ./build/omni_peer -p -H -P 9002 -i pc -b jetson -o /tmp/from_jetson.bin ``` 连接建立后,可在 A 端输入: ```text send start put /tmp/input.bin ``` 如需反向 `B -> A`,可在 B 端输入: ```text 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 ```bash ./build/omni_hub -p -P 9003 ``` D 端启动 Bridge: ```bash ./build/omni_bridge -p -H -P 9003 -i jetson -L 9004 ``` B 端连接 Bridge: ```bash ./build/omni_peer -p -H -P 9004 -i jetson -o /tmp/from_pc.bin ``` A 端连接 Hub: ```bash ./build/omni_peer -p -H -P 9003 -i pc -b jetson -o /tmp/from_jetson.bin ``` 连接建立后,可在 A 端输入: ```text send start put /tmp/input.bin ``` 如需反向 `B -> A`,可在 B 端输入: ```text 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_*` 是基于当前吞吐和缓冲/队列状态反推出来的估算值。进程空转时间很长、样本很小或者当前速率很低时,这两个值可能明显偏大。 - `propagation_*` 当前来自 `min_rtt_ms / 2` 的估算;如果当前协议没有 RTT 样本,这组字段就是 `0`。 - `end_to_end_*` 当前只在“最终接收文件的 peer”上有值,来源是发送端分片里的 `origin_ts_ms`。发送端、Hub、Bridge 一般是 `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` 上有值;发送端 / Hub / Bridge 多数为 `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 只有在拿到对应协议样本后才有值 | | 时间分辨率太粗 | `send_call_*`、`proto_send_avg_ms`、`processing_*` | 当前很多路径按毫秒计时,本地回环下大量操作小于 `1ms`,所以会显示 `0` | | 当前没有任务 | `progress_*` | 只注册但没有文件传输时,这组字段自然是 `0` |