Rust异步DNS服务发现库async-dnssd的使用,支持mDNS和DNS-SD协议的高效网络设备发现

Rust异步DNS服务发现库async-dnssd的使用,支持mDNS和DNS-SD协议的高效网络设备发现

Rust CI crates.io MIT licensed

该库封装了实现dnssd的C库,用于在局域网或广域网上发现、发布和解析网络服务。它集成到异步的tokio框架中。

安装

在您的项目目录中运行以下Cargo命令:

cargo add async-dnssd

或者在您的Cargo.toml中添加以下行:

async-dnssd = "0.5.1"

使用示例

下面是一个完整的示例,展示如何使用async-dnssd进行mDNS服务发现:

use async_dnssd::*;
use tokio::stream::StreamExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 浏览_http._tcp服务
    let mut browse = browse("_http._tcp")?;
    
    // 处理服务发现事件
    while let Some(event) = browse.next().await {
        match event {
            // 发现新服务
            BrowseEvent::ServiceFound(service) => {
                println!("Service found: {:?}", service);
                
                // 解析服务详情
                let mut resolve = resolve(service)?;
                if let Some(ResolveEvent::ServiceResolved(info)) = resolve.next().await {
                    println!("Resolved service: {:?}", info);
                    
                    // 获取服务地址
                    let mut query = query_record(
                        info.host_target().to_string(),
                        RecordType::A,
                        info.interface_index(),
                    )?;
                    
                    if let Some(QueryRecordEvent::RecordQueryResult(r)) = query.next().await {
                        println!("Address: {:?}", r);
                    }
                }
            }
            // 服务消失
            BrowseEvent::ServiceLost(service) => {
                println!("Service lost: {:?}", service);
            }
            // 其他事件
            _ => {}
        }
    }
    
    Ok(())
}

完整示例

下面是一个更完整的示例,展示如何发现和解析本地网络上的打印机服务:

use async_dnssd::*;
use tokio::stream::StreamExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("开始发现本地网络上的打印机服务...");
    
    // 浏览_ipp._tcp服务(打印机服务)
    let mut browse = browse("_ipp._tcp")?;
    
    // 处理服务发现事件
    while let Some(event) = browse.next().await {
        match event {
            BrowseEvent::ServiceFound(service) => {
                println!("发现打印机服务: {:?}", service);
                
                // 解析服务详情
                let mut resolve = resolve(service)?;
                if let Some(ResolveEvent::ServiceResolved(info)) = resolve.next().await {
                    println!("打印机详细信息:");
                    println!("名称: {}", info.fullname());
                    println!("主机: {}", info.host_target());
                    println!("端口: {}", info.port());
                    println!("TXT记录: {:?}", info.txt_properties());
                    
                    // 查询IPv4地址
                    let mut query = query_record(
                        info.host_target().to_string(),
                        RecordType::A,
                        info.interface_index(),
                    )?;
                    
                    if let Some(QueryRecordEvent::RecordQueryResult(r)) = query.next().await {
                        println!("IP地址: {:?}", r);
                    }
                    
                    // 查询IPv6地址
                    let mut query_v6 = query_record(
                        info.host_target().to_string(),
                        RecordType::AAAA,
                        info.interface_index(),
                    )?;
                    
                    if let Some(QueryRecordEvent::RecordQueryResult(r)) = query_v6.next().await {
                        println!("IPv6地址: {:?}", r);
                    }
                }
            }
            BrowseEvent::ServiceLost(service) => {
                println!("打印机服务离线: {:?}", service);
            }
            _ => {}
        }
    }
    
    Ok(())
}

功能说明

  1. 服务浏览:使用browse()函数发现特定类型的服务
  2. 服务解析:使用resolve()获取服务的详细信息
  3. 记录查询:使用query_record()查询服务的地址记录

注意

  • 该库需要tokio运行时支持
  • 在不同的平台上可能有不同的实现(macOS使用其原生Bonjour实现)
  • MIT许可证允许自由使用和修改

1 回复

Rust异步DNS服务发现库async-dnssd使用指南

简介

async-dnssd是一个Rust实现的异步DNS服务发现库,支持mDNS(多播DNS)和DNS-SD(DNS服务发现)协议。它提供了高效的方式来发现本地网络中的设备和服务,特别适合IoT设备发现、局域网服务发现等场景。

主要特性

  • 完全异步实现,基于async/await语法
  • 支持mDNS协议
  • 支持DNS-SD协议
  • 跨平台支持(Windows/macOS/Linux)
  • 零成本抽象,高性能实现

安装

在Cargo.toml中添加依赖:

[dependencies]
async-dnssd = "0.3"
tokio = { version = "1.0", features = ["full"] }

基本使用方法

1. 服务注册

use async_dnssd::*;
use tokio::time::{sleep, Duration};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 注册一个HTTP服务
    let service = RegisterService::new("_http._tcp", 8080)
        .with_name("My Rust Service")
        .with_txt_records(&[("path=/api", "version=1.0")])
        .register()
        .await?;

    println!("Service registered. Press Ctrl+C to stop...");
    
    // 保持服务运行
    loop {
        sleep(Duration::from_secs(1)).await;
    }
    
    // 服务会在退出时自动注销
    // 也可以手动调用 service.unregister().await?;
}

2. 服务发现

use async_dnssd::*;
use tokio::time::{sleep, Duration};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 浏览特定类型的服务
    let browser = BrowseService::new("_http._tcp")
        .browse()
        .await?;
    
    println!("Discovering HTTP services on local network...");
    
    // 处理发现的服务
    while let Some(event) = browser.next().await {
        match event {
            ServiceEvent::ServiceFound(service) => {
                println!("Found service: {}", service.name());
                
                // 解析服务详情
                if let Ok(resolved) = service.resolve().await {
                    println!("Service details: {:?}", resolved);
                    
                    // 获取TXT记录
                    if let Ok(txt) = resolved.get_txt_records().await {
                        println!("TXT records: {:?}", txt);
                    }
                }
            }
            ServiceEvent::ServiceLost(service) => {
                println!("Service lost: {}", service.name());
            }
        }
    }
    
    Ok(())
}

3. 主机名解析

use async_dnssd::*;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 解析主机名
    let hostname = "my-computer.local";
    if let Ok(addrs) = resolve_hostname(hostname).await {
        println!("Resolved {} to: {:?}", hostname, addrs);
    }
    
    Ok(())
}

高级用法

自定义网络接口

use async_dnssd::*;
use std::net::Ipv4Addr;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 指定网络接口进行服务发现
    let interface = Ipv4Addr::new(192, 168, 1, 10);
    let browser = BrowseService::new("_http._tcp")
        .on_interface(interface)
        .browse()
        .await?;
    
    // ...处理发现的服务
    Ok(())
}

组合查询

use async_dnssd::*;
use futures::stream::StreamExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 同时发现多种服务类型
    let http_browser = BrowseService::new("_http._tcp").browse().await?;
    let printer_browser = BrowseService::new("_printer._tcp").browse().await?;
    
    let mut combined = futures::stream::select(http_browser, printer_browser);
    
    while let Some(event) = combined.next().await {
        match event {
            ServiceEvent::ServiceFound(service) => {
                println!("Found service: {}", service.name());
            }
            ServiceEvent::ServiceLost(service) => {
                println!("Service lost: {}", service.name());
            }
        }
    }
    
    Ok(())
}

完整示例代码

下面是一个完整的服务注册和发现的示例:

use async_dnssd::*;
use tokio::time::{sleep, Duration};
use futures::stream::StreamExt;

// 服务注册示例
async fn register_service() -> Result<(), Box<dyn std::error::Error>> {
    // 注册HTTP服务
    let service = RegisterService::new("_http._tcp", 8080)
        .with_name("Rust Test Service")
        .with_txt_records(&[("path=/test", "version=1.0")])
        .register()
        .await?;

    println!("Service registered successfully");
    
    // 保持服务运行30秒
    sleep(Duration::from_secs(30)).await;
    
    // 注销服务
    service.unregister().await?;
    println!("Service unregistered");
    
    Ok(())
}

// 服务发现示例
async fn discover_services() -> Result<(), Box<dyn std::error::Error>> {
    // 创建浏览器实例
    let browser = BrowseService::new("_http._tcp")
        .browse()
        .await?;
    
    println!("Starting service discovery...");
    
    // 处理发现的服务事件
    while let Some(event) = browser.next().await {
        match event {
            ServiceEvent::ServiceFound(service) => {
                println!("[Found] Service: {}", service.name());
                
                // 解析服务详情
                match service.resolve().await {
                    Ok(resolved) => {
                        println!("  Host: {}", resolved.hostname());
                        println!("  Port: {}", resolved.port());
                        println!("  IP: {:?}", resolved.ip_addresses());
                        
                        // 获取TXT记录
                        if let Ok(txt) = resolved.get_txt_records().await {
                            println!("  TXT: {:?}", txt);
                        }
                    }
                    Err(e) => println!("  Failed to resolve: {}", e),
                }
            }
            ServiceEvent::ServiceLost(service) => {
                println!("[Lost] Service: {}", service.name());
            }
        }
    }
    
    Ok(())
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 在单独的task中运行服务注册
    tokio::spawn(async {
        if let Err(e) = register_service().await {
            eprintln!("Service registration error: {}", e);
        }
    });
    
    // 运行服务发现
    discover_services().await?;
    
    Ok(())
}

注意事项

  1. 在Linux系统上可能需要安装avahi-daemon服务
  2. 确保网络允许mDNS流量(UDP端口5353)
  3. 服务发现可能会有几秒钟的延迟
  4. 在生产环境中应考虑添加超时处理

性能优化建议

  1. 重用浏览器实例而不是频繁创建新的
  2. 对结果进行缓存,避免重复解析
  3. 合理设置查询间隔,避免过于频繁的查询
  4. 考虑使用filter方法提前过滤不需要的服务

async-dnssd为Rust开发者提供了强大而灵活的网络服务发现能力,特别适合构建需要自动发现局域网设备的应用程序。

回到顶部