Rust多播DNS库libmdns的使用,实现局域网设备发现与服务注册的高效mDNS协议支持
Rust多播DNS库libmdns的使用,实现局域网设备发现与服务注册的高效mDNS协议支持
libmdns是一个纯Rust实现的mDNS(RFC 6762)和DNS-SD(RFC 6763)协议库。这意味着在响应5353端口上UDP广播的DNS请求时,libmdns会广播一个DNS响应,广告你注册的服务。
该库基于tokio异步运行时构建,可以在专用线程中运行,也可以使用现有的tokio Handle来生成。
使用方式
要在项目中使用,请在Cargo.toml
中添加以下依赖:
[dependencies]
libmdns = "0.9"
完整示例
以下是使用libmdns实现服务注册和设备发现的完整示例代码:
use libmdns::{Responder, Service};
use std::time::Duration;
#[tokio::main]
async fn main() {
// 创建mDNS响应器
let responder = Responder::new().unwrap();
// 注册一个服务
let service = responder.register(
"_http._tcp".to_owned(), // 服务类型
"My Service".to_owned(), // 服务名称
8080, // 端口号
&["path=/"], // 文本记录
);
println!("Service registered: My Service._http._tcp.local:8080");
// 保持服务运行
loop {
tokio::time::sleep(Duration::from_secs(10)).await;
}
}
设备发现示例
以下是使用libmdns进行局域网设备发现的示例代码:
use libmdns::{Responder, Service};
use std::time::Duration;
#[tokio::main]
async fn main() {
// 创建mDNS响应器
let responder = Responder::new().unwrap();
// 监听特定服务类型
let browser = responder.browse("_http._tcp").unwrap();
println!("Discovering _http._tcp services...");
// 处理发现的服务
while let Ok(service) = browser.recv().await {
println!("Discovered service: {:?}", service);
}
}
依赖要求
libmdns支持的最低Rust工具链版本是1.70.0,但在旧版本的Rust上也可能编译成功。
该库建立在tokio 1.0运行时之上。
注意事项
- 该库最初是plietar/rust-mdns的一个分支
- 欢迎任何形式的贡献
- 目前没有人在积极改进它,问题可能不会立即修复
- 如果你愿意提交Pull Request来改进项目,会尽力进行审查和合并
通过使用libmdns库,你可以轻松地在Rust中实现mDNS协议支持,实现局域网内的服务发现和注册功能。
完整示例demo
服务注册与发现整合示例
以下是一个结合服务注册和设备发现的完整示例:
use libmdns::{Responder, Service};
use std::time::Duration;
use tokio::task;
#[tokio::main]
async fn main() {
// 创建mDNS响应器
let responder = Responder::new().unwrap();
// 服务注册任务
let register_task = task::spawn(async move {
// 注册一个HTTP服务
let _service = responder.register(
"_http._tcp".to_owned(), // 服务类型
"Rust mDNS Demo".to_owned(), // 服务名称
8080, // 端口号
&["path=/api"], // 文本记录
);
println!("[Service] Registered: Rust mDNS Demo._http._tcp.local:8080");
// 保持服务注册
loop {
tokio::time::sleep(Duration::from_secs(10)).await;
}
});
// 服务发现任务
let discover_task = task::spawn(async {
// 创建新的响应器用于发现服务
let responder = Responder::new().unwrap();
let browser = responder.browse("_http._tcp").unwrap();
println!("[Discovery] Starting to browse for _http._tcp services...");
// 持续发现服务
while let Ok(service) = browser.recv().await {
println!("[Discovery] Found service: {:?}", service);
}
});
// 等待两个任务完成
tokio::try_join!(register_task, discover_task).unwrap();
}
带自定义参数的设备发现示例
use libmdns::{Responder, Service};
use std::time::Duration;
#[tokio::main]
async fn main() {
// 创建mDNS响应器
let responder = Responder::new().unwrap();
// 监听打印机服务
let browser = responder.browse("_printer._tcp").unwrap();
println!("Searching for printers on local network...");
// 处理发现的服务,带超时设置
loop {
tokio::select! {
Ok(service) = browser.recv() => {
println!("Found printer: {:?}", service);
// 这里可以解析服务的具体信息
if let Some(addr) = service.addresses().next() {
println!("Printer IP: {}", addr);
}
}
_ = tokio::time::sleep(Duration::from_secs(30)) => {
println!("Discovery timeout reached");
break;
}
}
}
}
多服务注册示例
use libmdns::{Responder, Service};
use std::time::Duration;
#[tokio::main]
async fn main() {
let responder = Responder::new().unwrap();
// 注册HTTP服务
let http_service = responder.register(
"_http._tcp".to_owned(),
"Web Server".to_owned(),
80,
&["path=/", "version=1.0"],
);
// 注册自定义服务
let custom_service = responder.register(
"_myapp._tcp".to_owned(),
"Rust App".to_owned(),
3000,
&["id=123", "secure=true"],
);
println!("Services registered:");
println!("- Web Server._http._tcp.local:80");
println!("- Rust App._myapp._tcp.local:3000");
// 保持运行
loop {
tokio::time::sleep(Duration::from_secs(60)).await;
}
}
1 回复
Rust多播DNS库libmdns使用指南
概述
libmdns是一个Rust实现的轻量级多播DNS(mDNS)库,支持局域网设备发现和服务注册功能。它实现了DNS-Based Service Discovery协议(DNS-SD),允许设备在局域网内无需中央服务器即可相互发现和通信。
主要特性
- 零配置网络服务发现
- 支持服务注册和浏览
- 跨平台支持(Windows, Linux, macOS)
- 异步API设计
- 轻量级实现
完整示例代码
1. 服务注册与发现的完整示例
use libmdns::{Responder, Service, Discovery};
use std::time::Duration;
use tokio::time::sleep;
#[tokio::main]
async fn main() {
// 第一部分:服务注册
tokio::spawn(async {
let responder = Responder::builder()
.ttl(Duration::from_secs(60)) // 设置TTL为60秒
.build()
.unwrap();
let service = responder.register(
"_customservice._tcp".to_owned(),
"Rust mDNS Demo".to_owned(),
7878,
&["version=0.1.0", "author=example"],
);
println!("[Server] Service registered at port 7878");
sleep(Duration::from_secs(3600)).await;
});
// 第二部分:服务发现
let discovery = Discovery::new().unwrap();
let mut browser = discovery.browse("_customservice._tcp").unwrap();
println!("[Client] Discovering services...");
// 设置10秒超时
if let Ok(service) = tokio::time::timeout(
Duration::from_secs(10),
browser.recv_async()
).await {
if let Ok(service) = service {
println!("[Client] Found service: {}", service.name());
if let Ok(full_service) = service.get_full_service().await {
println!("[Client] Full details:");
println!(" Host: {}", full_service.host_name());
println!(" IP: {}", full_service.ip_addr());
println!(" Port: {}", full_service.port());
println!(" TXT: {:?}", full_service.txt());
}
}
} else {
println!("[Client] No services found within 10 seconds");
}
}
2. 多服务注册与过滤示例
use libmdns::{Responder, Discovery};
use std::time::Duration;
#[tokio::main]
async fn main() {
// 创建响应器
let responder = Responder::new().unwrap();
// 注册多个服务
responder.register(
"_printer._tcp".to_owned(),
"Office Printer".to_owned(),
631,
&["color=true", "duplex=true"],
);
responder.register(
"_printer._tcp".to_owned(),
"Home Printer".to_owned(),
9100,
&["color=false", "duplex=false"],
);
println!("Registered two printer services");
// 服务发现部分
let discovery = Discovery::new().unwrap();
let mut browser = discovery.browse("_printer._tcp").unwrap();
while let Ok(service) = browser.recv_async().await {
// 只显示支持彩色的打印机
if let Ok(full_service) = service.get_full_service().await {
if full_service.txt().iter().any(|s| s == "color=true") {
println!("Found color printer: {}", service.name());
println!(" IP: {}", full_service.ip_addr());
println!(" Port: {}", full_service.port());
}
}
}
}
关键点说明
- 异步设计:所有API都是异步的,需要配合tokio运行时使用
- 服务类型格式:必须遵循
_service._protocol
格式(如_http._tcp
) - TXT记录:可以提供任意数量的键值对作为服务元数据
- 平台兼容性:代码在三大主流操作系统上行为一致
- 错误处理:示例中简化了错误处理,实际使用时应该添加更完善的错误处理
实际应用场景
- 物联网设备发现:智能家居设备自动发现
- 本地服务发现:开发环境中微服务的自动注册与发现
- 打印机/设备共享:局域网内共享设备的自动发现
- 游戏联机:本地多人游戏的自动匹配