327 lines
17 KiB
Markdown
327 lines
17 KiB
Markdown
# 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 服务器
|
||
|
||
下面示例中的 `<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 端监听:
|
||
|
||
```bash
|
||
./build/omni_peer -M direct -p <proto> -L 9001 -i jetson -o /tmp/from_pc.bin
|
||
```
|
||
|
||
A 端连接:
|
||
|
||
```bash
|
||
./build/omni_peer -M direct -p <proto> -H <B_IP> -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 <proto> -P 9002
|
||
```
|
||
|
||
B 端连接 Hub:
|
||
|
||
```bash
|
||
./build/omni_peer -p <proto> -H <C_IP> -P 9002 -i jetson -o /tmp/from_pc.bin
|
||
```
|
||
|
||
A 端连接 Hub:
|
||
|
||
```bash
|
||
./build/omni_peer -p <proto> -H <C_IP> -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 <proto> -P 9003
|
||
```
|
||
|
||
D 端启动 Bridge:
|
||
|
||
```bash
|
||
./build/omni_bridge -p <proto> -H <C_IP> -P 9003 -i jetson -L 9004
|
||
```
|
||
|
||
B 端连接 Bridge:
|
||
|
||
```bash
|
||
./build/omni_peer -p <proto> -H <D_IP> -P 9004 -i jetson -o /tmp/from_pc.bin
|
||
```
|
||
|
||
A 端连接 Hub:
|
||
|
||
```bash
|
||
./build/omni_peer -p <proto> -H <C_IP> -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_*` 是基于最近活跃窗口的吞吐和缓冲/队列状态反推出来的估算值。最近没有足够流量样本、样本太小或者当前速率太低时,这两个值可能直接为 `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` |
|