一个轻量级、跨平台的 C++ 局域网设备发现库,用于在同一子网内自动广播自身并发现其他节点。
- 跨平台:支持 Windows、Linux、macOS
- 广播与组播:支持 UDP broadcast 和 multicast 两种发现方式
- 轻量:仅依赖 C++ 标准库和系统 socket API
- 线程安全:发送与接收运行在后台线程中,公开 API 可安全并发调用
- 可配置:支持端口、广播间隔、设备 TTL、自发现和去重模式等参数
- 可携带用户数据:每个节点可附带最多 4KB 的自定义字符串
mkdir build
cd build
cmake ..
cmake --build .| 选项 | 默认值 | 说明 |
|---|---|---|
discovery_BUILD_SHARED |
OFF |
构建共享库 |
discovery_BUILD_EXAMPLES |
ON |
构建示例程序 |
add_subdirectory(discovery)
target_link_libraries(your_target PRIVATE discovery::discovery)find_package(discovery REQUIRED)
target_link_libraries(your_target PRIVATE discovery::discovery)#include <iostream>
#include "discovery/discovery_peer.h"
int main() {
discovery::PeerParameters params;
params.setPort(12345);
params.setApplicationId(1001);
params.setCanDiscover(true);
params.setCanBeDiscovered(true);
discovery::Peer peer;
if (!peer.start(params, "my-device-name")) {
return 1;
}
for (const auto& discoveredPeer : peer.listDiscovered()) {
std::cout << discovery::ipToString(discoveredPeer.ipPort().ip())
<< " - " << discoveredPeer.userData() << '\n';
}
peer.stopAndWait();
return 0;
}discovery::PeerParameters params;
params.setPort(12345);
params.setApplicationId(1001);
params.setCanDiscover(true);
params.setCanBeDiscovered(false);
discovery::Peer peer;
peer.start(params, "");discovery::PeerParameters params;
params.setPort(12345);
params.setApplicationId(1001);
params.setCanDiscover(false);
params.setCanBeDiscovered(true);
discovery::Peer peer;
peer.start(params, "server-node");discovery::PeerParameters params;
params.setPort(12345);
params.setApplicationId(1001);
params.setCanUseBroadcast(false);
params.setCanUseMulticast(true);
params.setMulticastGroupAddress(0xE00000FB); // 224.0.0.251
discovery::Peer peer;
peer.start(params, "multicast-peer");PeerParameters 用于配置节点角色、网络传输模式和生命周期参数。
| 方法 | 说明 |
|---|---|
setPort(uint16_t) |
设置发现服务使用的端口 |
setApplicationId(uint32_t) |
设置应用 ID,用于隔离不同业务 |
setCanDiscover(bool) |
是否接收并发现其他设备 |
setCanBeDiscovered(bool) |
是否广播自身存在 |
setCanUseBroadcast(bool) |
是否启用 UDP 广播,默认 true |
setCanUseMulticast(bool) |
是否启用 UDP 组播,默认 false |
setMulticastGroupAddress(uint32_t) |
设置组播地址,主机字节序 |
setSendTimeout(std::chrono::milliseconds) |
设置广播间隔,默认 5000ms |
setDiscoveredPeerTtl(std::chrono::milliseconds) |
设置已发现设备 TTL,默认 10000ms |
setDiscoverSelf(bool) |
是否将自己计入发现结果,默认 false |
setSamePeerMode(SamePeerMode) |
设置去重模式:kIp 或 kIpAndPort |
validate() |
校验参数是否合法,成功时返回空字符串 |
Peer 是发现服务的主入口,负责启动、停止、广播与接收。
| 方法 | 说明 |
|---|---|
start(params, userData) |
启动发现服务;失败时返回 false |
stop() |
请求停止并立即返回 |
stopAndWait() |
请求停止并阻塞等待后台线程退出 |
setUserData(string) |
动态更新广播给其他设备的用户数据 |
listDiscovered() |
返回当前已发现设备的快照列表 |
DiscoveredPeer 表示一个已发现的远端节点。
| 方法 | 说明 |
|---|---|
ipPort() |
返回节点的 IP 地址与端口 |
userData() |
返回节点附带的用户数据 |
lastReceivedPacket() |
返回最后一次更新用户数据的快照序号 |
lastUpdated() |
返回最后收到数据包的时间戳(ms) |
/// 将主机字节序 IPv4 地址转换为 "A.B.C.D"
std::string ipToString(uint32_t ip);
/// 将端点转换为 "A.B.C.D:port"
std::string ipPortToString(const IpPort& ipPort);
/// 判断两个设备列表在指定去重模式下是否等价
bool isSame(SamePeerMode mode, const std::list<DiscoveredPeer>& lhs,
const std::list<DiscoveredPeer>& rhs);协议头由固定魔数 DSCV 和版本号组成,后续跟随固定长度字段与可变长用户数据。
0 1 2 3 4 5 6 7
+-------+-------+-------+-------+-------+-------+-------+-------+
| 'D' | 'S' | 'C' | 'V' | Ver | Reserved |
+-------+-------+-------+-------+-------+-------+-------+-------+
| PktType | Application ID |
+-------+-------+-------+-------+-------+-------+-------+-------+
| Peer ID | Snapshot Index |
+-------+-------+-------+-------+-------+-------+-------+-------+
| Snapshot Index (cont.) | User Data Size |
+-------+-------+-------+-------+-------+-------+-------+-------+
| User Data ... |
+-------+-------+-------+-------+-------+-------+-------+-------+
所有多字节整数均采用大端字节序。
| 字段 | 大小 | 说明 |
|---|---|---|
| Magic | 4 字节 | 固定为 DSCV |
| Version | 1 字节 | 当前版本号,固定为 1 |
| Reserved | 3 字节 | 保留字段,当前固定为 0 |
| Packet Type | 1 字节 | 数据包类型 |
| Application ID | 4 字节 | 应用标识,仅相同 ID 的节点彼此可见 |
| Peer ID | 4 字节 | 节点随机 ID,用于区分不同实例 |
| Snapshot Index | 8 字节 | 单调递增的包序号,用于丢弃旧包 |
| User Data Size | 2 字节 | 用户数据长度,最大 4096 |
| User Data | 可变 | 用户自定义内容 |
| 类型 | 值 | 说明 |
|---|---|---|
kIAmHere |
0 | 周期性存在广播 |
kIAmOutOfHere |
1 | 主动下线广播 |
discovery/
├── include/
│ └── discovery/
│ ├── discovery_discovered_peer.h
│ ├── discovery_ip_port.h
│ ├── discovery_peer.h
│ ├── discovery_peer_parameters.h
│ └── discovery_protocol.h
├── src/
│ ├── discovery_ip_port.cpp
│ ├── discovery_peer_env.cpp
│ └── discovery_protocol.cpp
├── examples/
│ ├── CMakeLists.txt
│ └── main.cpp
├── cmake/
│ └── discoveryConfig.cmake.in
├── Doxyfile
└── CMakeLists.txt
- 请确保防火墙允许对应 UDP 端口的广播或组播流量。
- 广播通常只在同一子网内有效;组播依赖网络设备支持。
- 只有
applicationId相同的节点才会互相发现。 - 如果启用组播,请确保
setMulticastGroupAddress()配置的是合法组播地址。