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运行时之上。

注意事项

  1. 该库最初是plietar/rust-mdns的一个分支
  2. 欢迎任何形式的贡献
  3. 目前没有人在积极改进它,问题可能不会立即修复
  4. 如果你愿意提交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());
            }
        }
    }
}

关键点说明

  1. 异步设计:所有API都是异步的,需要配合tokio运行时使用
  2. 服务类型格式:必须遵循_service._protocol格式(如_http._tcp)
  3. TXT记录:可以提供任意数量的键值对作为服务元数据
  4. 平台兼容性:代码在三大主流操作系统上行为一致
  5. 错误处理:示例中简化了错误处理,实际使用时应该添加更完善的错误处理

实际应用场景

  1. 物联网设备发现:智能家居设备自动发现
  2. 本地服务发现:开发环境中微服务的自动注册与发现
  3. 打印机/设备共享:局域网内共享设备的自动发现
  4. 游戏联机:本地多人游戏的自动匹配
回到顶部