概述
VLink Proxy 是一个基于 DDS 传输的中间层代理系统,专为跨网段通信监控、数据录制与回放、远程注入等场景设计。它由三个核心组件构成。
**相关文档**:可视化工具 vlink-viewer 使用 ProxyAPI 连接参见 14-viewer.md;CLI 监控工具参见 13-cli-tools.md;服务发现机制参见 17-discovery.md。
核心组件:
- **vlink-proxy**:独立运行的代理守护进程(可执行文件)
- **ProxyServer**:代理服务端库,可嵌入到应用进程中
- **ProxyAPI**:代理客户端库,供监控工具、CLI 工具、上位机连接使用
为什么需要代理层
VLink 的原生传输(shm、dds 等)要求通信双方在同一网段或 DDS 域内可直接发现彼此。在以下场景中需要引入代理层:
| 场景 | 说明 |
| 跨网段/跨 VLAN 通信 | 测试台架与开发机、调试机与目标机之间存在路由/防火墙隔离(单播 DDS 发现,非 NAT 穿透) |
| 远程监控与可视化 | 上位机工具(如 vlink-monitor)需要实时查看话题数据和统计信息 |
| 数据录制与回放 | 代理服务器代为订阅所有话题并将数据转发给录制客户端 |
| 数据注入与仿真 | 回放或仿真工具通过代理向车端节点注入消息 |
| 跨进程自动路由(Auto 模式) | 在无需手动配置路由的情况下自动订阅并转发指定话题 |
架构概览
Proxy 架构
通信信道说明
| 信道 | 方向 | 传输方式 | 安全加密 | 说明 |
| Control | Client -> Server | DDS(安全) | 是 | 客户端发送模式控制指令 |
| Time | Server -> Client | DDS(安全) | 是 | 心跳,每秒一次,携带版本/时间/CPU |
| InfoList | Server -> Client | DDS(安全) | 是 | 每秒一次的话题统计列表 |
| Data | 双向 | DDS 或 SHM(直连) | 否 | 原始消息载荷转发 |
通信流程与时序
Proxy 通信时序
Proxy 系统的通信分为六个阶段:
Phase 1: 节点发现
VLink 业务节点在启动时通过 DiscoveryReporter 以 UDP 组播方式广播自身的元信息(URL、序列化类型、节点类型、PID、主机名)。ProxyServer 内部运行 DiscoveryViewer,持续监听组播地址 239.255.0.100,聚合所有在线节点的 URL 列表,并计算每个话题的频率、吞吐量、丢包率和延迟统计。
Phase 2: 客户端连接
ProxyServer 每秒通过 DDS 安全信道广播两类信息:
- **Time 心跳**:携带 VLINK_VERSION、主机名、CPU 占用、内存占用、系统时间(Unix epoch 微秒)和启动时长
- **InfoList**:所有已发现话题的统计数组(url、ser、status、freq、rate、loss、latency、process_list)
ProxyAPI 客户端订阅 Time 心跳,收到第一条心跳后触发 ConnectCallback(true),确认连接成功。同时开始接收 InfoList,更新本地话题列表。
Phase 3: 模式控制
kController 角色的客户端通过 send_control() 向 ProxyServer 发送 Control 指令,指定工作模式和关注的 URL 列表。ProxyServer 收到后按指令订阅/取消订阅实际话题。
Phase 4: 数据转发
ProxyServer 订阅的实际话题收到消息后,将原始数据封装为 ProxyData(url + ser + schema + raw + timestamp + seq),通过 Data 信道(DDS 或 SHM 直连)转发给所有已连接的 ProxyAPI 客户端。客户端的 DataCallback 被触发。
Phase 5: 前端推送
ProxyAPI 客户端收到数据后,根据自身类型执行不同的后续处理:
- **vlink-viewer**(桌面 GUI):通过 Qt Signal/Slot 将数据投递到 GUI 线程,渲染图像/点云/数据表
- **vlink-foxglove**(Web 可视化):通过 FoxgloveConverter 将数据转换为 Foxglove FlatBuffer Schema,再通过 WebSocket 推送到 Foxglove Studio 浏览器
- **vlink-rerun**(Web 可视化):通过 RerunConverter 将数据转换为 Rerun Archetype,再通过 RecordingStream::log() 推送到 Rerun Viewer
- **自定义工具**:在 DataCallback 中执行用户自定义逻辑
Phase 6: 断线与重连
ProxyAPI 客户端通过心跳检测连接状态。若连续 5 秒 未收到 Time 心跳,判定连接断开,触发 ConnectCallback(false)。kController 角色在重连成功后会自动重新发送最后一次 Control 指令,无需用户干预。
各工具在 Proxy 体系中的角色
| 工具/组件 | Proxy 角色 | 说明 |
| vlink-proxy | ProxyServer 守护进程 | 独立运行,聚合所有传输后端数据,转发给客户端 |
| ProxyServer(库) | 嵌入式服务端 | 可嵌入业务进程,无需独立守护进程 |
| vlink-viewer | ProxyAPI kController | 桌面 GUI,按需订阅话题,实时渲染图像/点云/数据 |
| vlink-player | 回放 GUI / 进程编排器 | Bag 回放界面;通过拉起 vlink-proxy 等子进程完成数据注入与联动 |
| vlink-analyzer | ProxyAPI kListener | 数据分析,只读监听统计信息 |
| vlink-foxglove | ProxyAPI kController | Foxglove 桥接,按需订阅(kAuto 模式),通过 WebSocket 推送 |
| vlink-rerun | ProxyAPI kController | Rerun 桥接,全量订阅(kAutoAndObserveAll),通过 gRPC 推送 |
| vlink-monitor | 不使用 Proxy(DiscoveryViewer) | CLI 工具,直接通过组播发现节点,不经过 Proxy |
| vlink-bag | BagReader / BagWriter | CLI 录制、回放、检查与转换工具,不通过 ProxyAPI 控制面工作 |
| 自定义监控工具 | ProxyAPI kController/kListener | 用户自行开发的监控、录制、注入工具 |
vlink-foxglove 和 vlink-rerun 在 Proxy 体系中都是 **ProxyAPI 客户端**,它们的角色等同于 vlink-viewer——通过 ProxyAPI 连接到 ProxyServer 获取数据。区别在于输出端:Viewer 输出到 Qt GUI,Foxglove 输出到 WebSocket,Rerun 输出到 gRPC。
ProxyServer 说明
ProxyServer 是代理系统的服务端,继承自 MessageLoop,以事件循环方式运行。
主要职责
- 运行 DiscoveryViewer,枚举 DDS 域内所有活跃的 Publisher/Subscriber/Server/Client/Setter/Getter
- 接收来自 ProxyAPI 客户端的 Control 指令,切换工作模式
- 每秒广播一次 Time 心跳(携带版本、主机名、CPU 占用、内存占用、时间戳)
- 每秒广播一次 InfoList(各话题的频率、吞吐量、丢包率、延迟统计)
- 在 Observe/Record/Auto 模式下,订阅实际话题并将数据转发给客户端
- 在 Play/Edit/Auto 模式下,接收客户端注入的数据并发布到实际话题
单例约束
每个操作系统进程中只能存在一个 ProxyServer 实例。重复构造将打印 Fatal 日志并直接返回,不会初始化任何通道。
ProxyServer::Config 字段说明
| 字段 | 类型 | 默认值 | 说明 |
| async | bool | false | 异步转发:在 MessageLoop 线程上发布数据,false 则在订阅回调中内联发布 |
| reliable | bool | false | 数据通道使用 DDS 可靠 QoS;必须与所有客户端一致 |
| enable_tcp | bool | false | 数据通道使用 TCP 传输;必须与所有客户端一致 |
| direct | bool | false | 使用 SHM(Iceoryx)作为数据通道,替代 DDS;必须与所有客户端一致 |
| native_mode | bool | false | 将所有 DDS 流量限制到 127.0.0.1(仅本机,用于本机测试) |
| domain_id | int | 0 | DDS 域 ID,范围 0~255,所有客户端必须使用相同的域 ID |
| buf_size | uint32_t | 0 | DDS socket 收发缓冲区大小(字节),0 使用内置默认值(8MB) |
| mtu_size | uint32_t | 0 | DDS 分片 MTU 大小(字节),0 使用内置默认值(65500 字节) |
| max_packet_size | double | 0 | 单条消息最大转发大小(MiB),超出则丢弃。**注意**:当前实现不存在"0 表示不限制"的特判,字段为 0 时实际会把所有非空消息都丢弃;要放行大包必须显式设置一个足够大的 MiB 值(CLI 默认 4.0,头文件注释与实现不符,以此处描述为准) |
| security_key | std::string | "" | Time/InfoList/Control 通道的安全加密密钥 |
| bind_ip | std::string | "" | DDS socket 绑定的本地 IP,空字符串表示绑定所有接口 |
| peer_ip | std::string | "" | DDS 单播对端 IP,空字符串使用多播发现 |
| dds_impl | std::string | "dds" | DDS 实现选择:如 "dds"(FastDDS)、"ddsc"(CycloneDDS)、"ddsr"、"ddst" |
| use_iox | bool | false | 是否在启动时内嵌启动 Iceoryx RouDi 守护进程 |
| iox_monitoring | bool | true | 是否启用 Iceoryx 监控/自省功能 |
| iox_strategy | int | 1 | Iceoryx 内存分配策略(1=低内存,2=中等,3=高内存),默认低内存 |
| iox_config | std::string | "" | Iceoryx TOML 配置文件路径,空字符串使用默认配置 |
| runnable_version_major | uint16_t | 1 | 加载的 runnable 插件(API 名为 RunablePluginInterface)所需的最低主版本号 |
| runnable_version_minor | uint16_t | 0 | 加载的 runnable 插件(API 名为 RunablePluginInterface)所需的最低次版本号 |
| runnable_prefix | std::string | "" | 插件共享库文件名前缀 |
| runnable_list | std::vector<std::string> | {} | 启动时加载的 runnable 插件名称列表(实际 API 类型名为 RunablePluginInterface) |
嵌入式使用示例
int main() {
server.quit(true);
});
server.run();
return 0;
}
static void init(const std::string &app_name="", const std::string &log_path="") noexcept
Initialises the logger singleton.
VLink proxy server daemon backed by a MessageLoop.
Definition proxy_server.h:131
Global singleton logger with three output styles and pluggable backends.
VLINK_EXPORT void register_terminate_signal(std::function< void(int)> &&callback, bool is_async=false, bool pass_through=false) noexcept
Registers a callback for graceful termination signals (SIGTERM, SIGINT, etc.).
VLink proxy server daemon – singleton per process.
Construction-time configuration for ProxyServer.
Definition proxy_server.h:169
bool direct
Use SHM channels for data (requires use_iox or external RouDi).
Definition proxy_server.h:173
std::string security_key
Security key for authenticated DDS channels.
Definition proxy_server.h:179
bool native_mode
Restrict all DDS traffic to loopback (127.0.0.1).
Definition proxy_server.h:174
int domain_id
DDS domain ID.
Definition proxy_server.h:175
bool reliable
Use reliable DDS QoS; must match all client configs.
Definition proxy_server.h:171
std::string dds_impl
DDS implementation transport.
Definition proxy_server.h:182
double max_packet_size
Maximum relayed payload in MiB; 0 = no limit.
Definition proxy_server.h:178
bool enable_tcp
Use TCP transport for DDS data channels.
Definition proxy_server.h:172
Platform-agnostic system utilities for process, thread, network and signal management.
vlink-proxy 命令行工具
vlink-proxy 是 ProxyServer 的独立可执行文件,直接在终端启动即可。
命令行参数
vlink-proxy [选项]
选项:
-a, --async 启用异步转发模式
-r, --reliable 启用可靠 QoS 模式
-t, --tcp 启用 TCP 传输
-g, --direct 启用 SHM 直连模式(需要 Iceoryx)
-d, --domain_id INT DDS 域 ID(0~255,默认 0)
-k, --key STRING 安全密钥
-b, --bind_ip STRING 绑定本地 IP 地址
-p, --peer_ip STRING 单播对端 IP 地址
-s, --buf_size UINT DDS 收发缓冲区大小(字节)
-e, --mtu_size UINT DDS MTU 大小(字节)
-n, --native 限制 DDS 流量到 127.0.0.1
-x, --max_packet_size FLOAT 单条消息最大大小(MiB,默认 4.0)
-c, --iox_config PATH Iceoryx TOML 配置路径(同时启用 use_iox)
-l, --iox_strategy INT Iceoryx 内存策略(1/2/3,CLI 默认 2)
-m, --iox_monitoring STR Iceoryx 监控(on/off,默认 on)
--dds_impl STRING DDS 实现(dds/ddsc/ddsr/ddst,默认 dds)
--runnable NAME... 加载 runnable 插件名称列表(API 名为 RunablePluginInterface)
典型启动示例
# 最简启动(默认配置)
vlink-proxy
# 指定域 ID 和安全密钥
vlink-proxy -d 1 -k "secure_key_2026"
# 启用 TCP + 可靠模式,绑定指定 IP,限制最大包大小为 8 MiB
vlink-proxy -r -t -b 192.168.1.100 -x 8.0
# 启用 SHM 直连模式(内嵌 RouDi,使用高内存策略)
vlink-proxy -g -c /etc/iceoryx/config.toml -l 3
# 仅本机通信(native 模式)
vlink-proxy -n
# 单播 DDS 发现(指定对端 IP 跳过多播)
vlink-proxy -b 10.0.0.1 -p 10.0.0.2
# 加载 runnable 插件(API 名为 RunablePluginInterface)
vlink-proxy --runnable my_plugin_a my_plugin_b
注意事项
- vlink-proxy 内置了单例检测,同一机器上不允许同时运行两个实例
- 如果需要 DDS 多播发现,可能需要为网络接口添加多播/广播路由规则(启动时会打印提示地址)
- 启动时环境变量 VLINK_INTRA_BIND(参见 21-environment-vars.md)若被设置,服务器将同时订阅 intra:// 话题
ProxyAPI 说明
ProxyAPI 是代理系统的客户端,继承自 MessageLoop,通过 DDS 连接到运行中的 ProxyServer。
角色
| 角色 | 枚举值 | 权限 |
| kController | 0 | 可调用 send_control() 和 send_data() |
| kListener | 1 | 只读,send_control() 和 send_data() 返回 false |
工作模式
| 模式 | 枚举值 | 说明 |
| kOffline | 0 | 离线,服务器释放所有订阅 |
| kObserveOne | 1 | 只观察 url_meta_list 中指定的单个话题 |
| kObserveAll | 2 | 观察 DDS 域内发现的全部话题 |
| kRecord | 3 | 录制 url_meta_list 中指定话题的数据 |
| kPlay | 4 | 回放:客户端通过 send_data() 注入,服务器转发 |
| kEdit | 5 | 编辑模式:服务器转发客户端注入的数据 |
| kAuto | 6 | 自动模式:观察指定话题并自动转发给订阅方 |
| kAutoAndObserveAll | 7 | Auto 模式 + 同时观察所有话题 |
错误码
| 错误码 | 枚举值 | 触发条件 |
| kNoError | 0 | 无错误,连接正常 |
| kModeError | 1 | 请求了不支持的工作模式 |
| kControlError | 2 | 服务器返回的 control_id 与客户端不匹配 |
| kReliableCompError | 3 | 客户端与服务器的 reliable 设置不一致 |
| kTcpCompError | 4 | 客户端与服务器的 enable_tcp 设置不一致 |
| kDirectCompError | 5 | 客户端与服务器的 direct 设置不一致 |
| kMultiProxyError | 7 | 同一 DDS 域内检测到多个 ProxyServer |
| kVersionCompError | 8 | 客户端与服务器的 VLINK_VERSION 字符串不匹配 |
| kUnknownError | 9 | 未分类错误 |
ProxyAPI::Config 字段说明
| 字段 | 类型 | 默认值 | 说明 |
| role | Role | kController | 客户端角色 |
| domain_id | int | 0 | DDS 域 ID,必须与服务器一致 |
| dds_impl | std::string | "dds" | DDS 实现选择 |
| security_key | std::string | "" | 安全密钥,必须与服务器 key 一致 |
| native | bool | false | 是否限制 DDS 流量到 127.0.0.1 |
| reliable | bool | false | 数据通道 QoS,必须与服务器一致 |
| direct | bool | false | 是否使用 SHM 直连,必须与服务器一致 |
| enable_tcp | bool | false | 是否使用 TCP,必须与服务器一致 |
| match_version | bool | true | 是否校验 VLINK_VERSION 版本字符串 |
| allow_ip | std::string | "" | 绑定本地 DDS socket IP |
| peer_ip | std::string | "" | 单播对端 IP |
| buf_size | int | 0 | Socket 缓冲区大小(字节),0 使用默认值 |
| mtu_size | int | 0 | DDS MTU 大小(字节),0 使用默认值 |
心跳与断线重连机制
- ProxyAPI 内部通过 TimeSub 订阅服务器每秒广播的心跳
- 若连续 5 秒未收到心跳,判定连接断开,触发 ConnectCallback(false)
- kController 角色会在断线重连后自动重新发送最后一次 Control 指令
连接配置详解
普通 DDS 模式(默认)
客户端与服务器均使用 DDS 传输数据,适合跨机器、跨网段场景。
@ kController
Definition proxy_api.h:229
Construction-time configuration for ProxyAPI.
Definition proxy_api.h:327
std::string security_key
Optional security key for encrypted DDS channels; must match the server key.
Definition proxy_api.h:331
bool match_version
Reject connections when the server's VLINK_VERSION differs from the client.
Definition proxy_api.h:336
int domain_id
DDS domain ID; must match the server's domain_id.
Definition proxy_api.h:329
bool direct
Use direct SHM channels for data; must match the server's direct setting.
Definition proxy_api.h:334
std::string dds_impl
DDS implementation transport: "dds", "ddsc", "ddsr", etc.
Definition proxy_api.h:330
bool enable_tcp
Use TCP transport for data channels; must match the server's enable_tcp.
Definition proxy_api.h:335
Role role
Role of this client instance.
Definition proxy_api.h:328
bool reliable
Use reliable DDS QoS; must match the server's reliable setting.
Definition proxy_api.h:333
SHM 直连模式(direct)
数据通道改走 Iceoryx 共享内存,零拷贝,延迟极低,适合同机监控。
单播 DDS 模式(跨子网)
std::string peer_ip
Unicast peer IP for DDS discovery (empty = multicast).
Definition proxy_api.h:338
std::string allow_ip
Bind DDS sockets to this IP address (empty = any).
Definition proxy_api.h:337
此模式是 DDS 层的单播发现(显式 peer IP),不做 NAT 穿透;双方仍需 IP 可达。真正需要 NAT 穿透请考虑 zenoh://。
本机测试模式(native)
bool native
When true, restricts all DDS traffic to 127.0.0.1 (loopback only).
Definition proxy_api.h:332
监控活跃节点、话题与消息统计
注册 InfoCallback
api.register_connect_callback([](bool connected) {
if (connected) {
} else {
}
});
api.register_info_callback([](const std::vector<vlink::ProxyAPI::Info>& list) {
for (const auto& info : list) {
for (const auto& proc : info.process_list) {
}
}
});
api.async_run();
Client-side proxy monitoring and control API backed by a MessageLoop.
Definition proxy_api.h:169
@ kListener
Definition proxy_api.h:229
话题状态说明
| 状态 | 含义 |
| kActive | 话题正在活跃接收数据 |
| kInActive | 话题存在但最近 2 秒内未收到数据 |
| kPending | 话题刚被发现,统计数据尚在累积中(前 10 秒) |
| kInvalid | 话题类型不支持观察(例如纯订阅端) |
获取服务器时间和系统信息
api.register_time_callback([](uint64_t sys_time, uint64_t boot_time) {
});
double cpu = api.get_current_cpu_usage();
double mem = api.get_current_memory_usage();
static std::string get_format_sys_time(uint64_t time, bool enable_utc=false)
Formats a microsecond wall-clock timestamp as a human-readable string.
static std::string get_format_boot_time(uint64_t time)
Formats a microsecond boot-time duration as a human-readable string.
通过 Proxy 进行跨网段通信
典型场景:车载计算单元(EdgePC)运行实际业务节点,开发机(DevPC)通过代理进行监控和数据注入。
[EdgePC 192.168.1.100] [DevPC 192.168.2.50]
Publisher<LidarData> vlink-monitor / 自定义工具
Subscriber<ControlCmd> |
| |
vlink-proxy -b 192.168.1.100 \ ProxyAPI::Config::
-p 192.168.2.50 \ allow_ip = "192.168.2.50"
-d 1 -k "key" peer_ip = "192.168.1.100"
domain_id = 1
security_key = "key"
启动步骤:
- 在 EdgePC 上启动代理服务器:
vlink-proxy -b 192.168.1.100 -p 192.168.2.50 -d 1 -k "key"
- 在 DevPC 上连接并开始观察所有话题:
api.send_control(ctrl);
});
api.async_run();
@ kObserveAll
Observe all discovered topics on the network.
Definition proxy_api.h:182
Control message sent from a kController client to ProxyServer.
Definition proxy_api.h:290
Mode mode
Target operation mode.
Definition proxy_api.h:291
Raw message payload delivered via DataCallback or sent via send_data().
Definition proxy_api.h:308
数据录制与回放
录制模式
};
api.send_control(ctrl);
});
@ kRecord
Record data from topics in the URL list.
Definition proxy_api.h:183
@ kProtobuf
Decode using the Protocol Buffers stack.
Definition types.h:188
@ kSubscriber
Event subscriber (receive broadcast).
Definition types.h:94
std::vector< UrlMeta > url_meta_list
Topics to observe / inject (mode-dependent).
Definition proxy_api.h:292
回放模式
};
api.send_control(ctrl);
data.
url =
"dds://sensor/lidar";
data.
ser =
"demo.proto.PointCloud";
api.send_data(data);
static Bytes deep_copy(uint8_t *data, size_t size, uint8_t offset=0) noexcept
Creates an owned deep copy of an external mutable buffer.
@ kPlay
Replay: inject previously recorded data.
Definition proxy_api.h:184
@ kPublisher
Event publisher (N-to-N broadcast).
Definition types.h:93
std::string url
Topic URL the data was captured on.
Definition proxy_api.h:309
int64_t seq
Publisher sequence number.
Definition proxy_api.h:314
Bytes raw
Raw serialised message bytes.
Definition proxy_api.h:312
SchemaType schema
Coarse schema family of the payload.
Definition proxy_api.h:311
int64_t timestamp
Elapsed time in microseconds since session start; -1 if unset.
Definition proxy_api.h:313
std::string ser
Serialisation type of the payload.
Definition proxy_api.h:310
ProxyData 零拷贝传输
当编译时启用 VLINK_PROXY_ENABLE_ZEROCOPY_DATA(默认开启),数据通道使用 zerocopy::ProxyData 结构体代替 Protobuf 序列化。
ProxyData 是一个 80 字节固定头部 + 变长尾部 的平坦内存结构:
[ magic_begin (4B) | ProxyData struct (80B) | raw + url + ser + hostname | magic_end (4B) ]
优点:
- 零额外分配:整个 payload 在一次 create() 调用中完成内存布局
- 零拷贝读取:url()、ser()、hostname() 返回 string_view,直接引用尾部内存
- 反序列化时(operator<<)不需要拷贝,直接借用 wire buffer
注意事项:
- ProxyData 的 string_view 字段的生命周期与持有它的 Bytes 对象绑定,不可在 Bytes 销毁后继续访问
- 32 位架构不支持(编译时会发出警告)
安全配置
代理系统的 Time、InfoList、Control 三个信道使用 DDS 安全扩展进行加密和认证。
配置安全密钥
服务器端:
客户端:
api_cfg.security_key = "my_32_byte_secret_key_for_aes_auth";
版本兼容校验
默认情况下(match_version = true),客户端会在第一次心跳时检查服务器的 VLINK_VERSION 字符串是否与自身一致。版本不匹配将触发 kVersionCompError 并拒绝建立连接。
如需跨版本连接(不推荐),可禁用:
话题过滤
ProxyAPI::Control 支持在服务器端进行话题过滤,减少不必要的订阅和数据转发。
api.send_control(ctrl);
bool filter_by_process
When true, filter_str matches process names; otherwise matches URLs.
Definition proxy_api.h:293
std::string filter_str
Space-separated filter keywords (case-insensitive).
Definition proxy_api.h:294
uint32_t filter_type
Type filter: 0=all, 1=pub+sub pair, 2=srv+cli pair, etc.
Definition proxy_api.h:295
注意:过滤功能由编译宏 VLINK_PROXY_ENABLE_FILTER(默认为 1)控制,可通过 ProxyAPI::is_enable_filter() 检查运行时是否启用。
与 CLI 工具的配合
vlink-monitor(参见 13-cli-tools.md)是 VLink 提供的命令行监控工具,内部使用 DiscoveryViewer 发现节点和话题。vlink-viewer(参见 14-viewer.md)则通过 ProxyAPI 连接到 vlink-proxy 获取详细数据。
典型工作流程:
1. 在目标设备上启动代理服务
$ vlink-proxy -d 0 -k "key"
2. 在开发机上启动 vlink-monitor(基于 DiscoveryViewer 发现)
$ vlink-monitor
3. 或使用 vlink-viewer 通过 ProxyAPI 以更丰富的功能连接
- 接收 Time 心跳(实时展示 CPU/内存/时间)
- 接收 InfoList(实时展示所有话题的频率/吞吐量/延迟/丢包)
- 按需发送 Control 指令观察特定话题的原始数据
完整使用示例:部署 + 连接 + 监控
部署端(EdgePC,运行业务节点 + 代理服务器)
int main() {
proxy_cfg.
bind_ip =
"192.168.1.100";
proxy_cfg.
peer_ip =
"192.168.2.50";
proxy.quit(true);
});
proxy.run();
return 0;
}
Type-safe publisher for the VLink event communication model.
Definition publisher.h:102
std::string peer_ip
Peer unicast IP for DDS; empty = multicast.
Definition proxy_server.h:181
std::string bind_ip
Local IP to bind DDS sockets; empty = any.
Definition proxy_server.h:180
监控端(DevPC)
#include <iostream>
int main() {
api.register_connect_callback([&api](bool connected) {
std::cout << (connected ? "[+] 代理服务器已连接" : "[-] 代理服务器断开") << std::endl;
if (connected) {
api.send_control(ctrl);
}
});
std::cerr << "代理错误码: " << static_cast<int>(err) << std::endl;
}
});
api.register_time_callback([](uint64_t sys_time, uint64_t boot_time) {
std::cout << "服务器时间: "
<< " | 运行时长: "
<< std::endl;
});
api.register_info_callback([](const std::vector<vlink::ProxyAPI::Info>& list) {
std::cout << "=== 话题列表 ===" << std::endl;
for (const auto& info : list) {
std::cout << " " << info.url
<< " schema=" << static_cast<int>(info.schema)
<< " freq=" << info.freq << " Hz"
<< " rate=" << info.rate / 1024 << " KB/s"
<< " loss=" << info.loss * 100 << "%"
<< " lat=" << info.latency << " ms"
<< std::endl;
}
});
std::cout <<
"收到数据: " << data.
url
<<
" schema=" <<
static_cast<int>(data.
schema)
<<
" [" << data.
raw.
size() <<
" bytes]"
<< std::endl;
});
api.async_run();
api.wait_for_quit();
return 0;
}
size_t size() const noexcept
Returns the number of usable bytes (excluding the prefix offset region).
Definition bytes.h:868
Error
Compatibility and protocol error codes reported via ErrorCallback.
Definition proxy_api.h:198
@ kNoError
No error; connection is healthy.
Definition proxy_api.h:199
Client-side VLink proxy monitoring and control API.
CMake 集成
# CMakeLists.txt
# 仅需要客户端(ProxyAPI)
find_package(vlink REQUIRED)
target_link_libraries(my_monitor PRIVATE vlink::proxy_api)
# 需要服务端(ProxyServer)
target_link_libraries(my_server PRIVATE vlink::proxy_server)
# 通常不需要手动链接 vlink-proxy 可执行文件,直接使用系统安装的版本即可
编译宏说明:
| 宏 | 含义 |
| VLINK_ENABLE_PROXY | 由 proxy_api/proxy_server 库自动添加,标识代理功能已启用 |
| VLINK_PROXY_API_LIBRARY | 动态库构建时由 proxy_api 添加 |
| VLINK_PROXY_SERVER_LIBRARY | 动态库构建时由 proxy_server 添加 |
常见问题与注意事项
- **reliable/enable_tcp/direct 三个配置必须客户端与服务器完全一致**,任何一个不匹配都会在心跳校验时触发对应的错误码(kReliableCompError 等),客户端将拒绝连接。
- **同一 DDS 域内只能运行一个 ProxyServer**,否则 ProxyAPI 会检测到 kMultiProxyError。每个域需要独立使用不同的 domain_id。
- **DataCallback 中的 Data::raw 是浅拷贝**,仅在回调执行期间有效。若需在回调外使用数据,必须进行深拷贝(data.raw.deep_copy(...) 或使用 std::vector<uint8_t>)。
- **send_data() 返回 false 不一定是链路故障**,也可能是对端暂时没有订阅者,或者调用方没有显式传入完整的 ser + schema 路由元数据。
- **ProxyServer 的析构是同步阻塞的**,会等待所有 DDS 句柄和 DiscoveryViewer 完成清理后才返回,应确保在进程退出前调用。
- **SHM 直连模式(direct)需要 Iceoryx RouDi 守护进程已运行**,可以通过 -c 参数让 vlink-proxy 自动内嵌启动,也可以外部单独启动 iox-roudi。
- **max_packet_size=0 不是"不限制"**。过滤逻辑是 if (bytes.size() > real_max_packet_size) return;(real_max_packet_size = max_packet_size * 1024 * 1024),没有 0 = unlimited 的特判;字段为 0 时所有非空消息都会被丢弃。若要放行大包请显式设置足够大的 MiB 值(CLI 默认 4.0)。