#mdns #discovery #dns-client #dns #user-interface #lan #server-client

searchlight

📡 基于 Rust 的 mDNS 服务器和客户端库,注重用户界面设计

4 个版本

0.3.2 2023 年 9 月 26 日
0.3.1 2023 年 4 月 22 日
0.3.0 2023 年 1 月 8 日
0.2.0 2022 年 12 月 30 日
0.1.0 2022 年 12 月 18 日

#1407网络编程 中排名

每月 28 次下载

MIT/Apache

76KB
1.5K SLoC

License crates.io docs.rs Workflow Status

📡 Searchlight

Searchlight 是一个简单、轻量级且易于使用的 mDNS 服务器和客户端库,即使您只有基本的 mDNS 知识也可以轻松使用。

简单来说,Searchlight 是一个用于在本地网络上广播和发现“服务”的库。这项技术与 Chromecast、AirDrop、Philips Hue 等产品使用的相同技术。

Searchlight 注重用户界面设计。 该库的显著特点是它跟踪网络中服务的存在,并在它们出现和消失时通知您,这样您可以相应地更新您的用户界面,提供响应速度快、直观且熟悉的用户体验,类似于 WiFi、蓝牙、Chromecast 等扫描列表。

  • 🌐 IPv4 和 IPv6 - 支持 IPv4 和 IPv6。
  • ✨ 操作系统支持 - 支持 Windows、macOS 以及大多数 UNIX 系统。
  • 📡 广播 - 向网络发送服务公告并响应发现请求。(mDNS 服务器)
  • 👽 发现 - 发现网络上的服务并跟踪它们的存在。(mDNS 客户端)
  • 🧵 单线程 - 由于使用了 Tokio 异步运行时和任务调度器,Searchlight 只在单个线程上运行。
  • 🤸 灵活的 API - 无异步、无流、无通道、无废话。只需提供一个事件处理函数,然后根据您的喜好在应用程序和 Searchlight 之间建立桥梁。
  • 👻 背景运行时 - 发现和广播都可以在单独的线程上后台运行,提供必要的优雅关闭句柄。
  • 📨 UDP - 所有网络,包括发现和广播,都是无连接的,并通过 UDP 完成。
  • 🔁 Loopback - 支持接收由同一套接字发送的数据包,旨在用于测试。
  • 🎯 接口定向 - 支持针对特定网络接口进行发现和广播。

用法

将Searchlight添加到您的Cargo.toml文件中

[dependencies]
searchlight = "0.3.1"

要了解更多关于如何使用Searchlight的信息,请参阅文档

示例

👽 发现

在网络中找到所有的Chromecast设备。

use searchlight::{
    discovery::{DiscoveryBuilder, DiscoveryEvent},
    dns::{op::DnsResponse, rr::RData},
    net::IpVersion,
};

fn get_chromecast_name(dns_packet: &DnsResponse) -> String {
    dns_packet
        .additionals()
        .iter()
        .find_map(|record| {
            if let Some(RData::SRV(_)) = record.data() {
                let name = record.name().to_utf8();
                let name = name.strip_suffix('.').unwrap_or(&name);
                let name = name.strip_suffix("_googlecast._tcp.local").unwrap_or(&name);
                let name = name.strip_suffix('.').unwrap_or(&name);
                Some(name.to_string())
            } else {
                None
            }
        })
        .unwrap_or_else(|| "Unknown".into())
}

DiscoveryBuilder::new()
    .service("_googlecast._tcp.local.")
    .unwrap()
    .build(IpVersion::Both)
    .unwrap()
    .run(|event| match event {
        DiscoveryEvent::ResponderFound(responder) => {
            println!(
                "Found Chromecast {} at {}",
                get_chromecast_name(&responder.last_response),
                responder.addr.ip()
            );
        }
        DiscoveryEvent::ResponderLost(responder) => {
            println!(
                "Chromecast {} at {} has gone away",
                get_chromecast_name(&responder.last_response),
                responder.addr.ip()
            );
        }
        DiscoveryEvent::ResponseUpdate { .. } => {}
    })
    .unwrap();

📡 广播

在网络中广播一项服务,并验证其是否可被发现。

use searchlight::{
    broadcast::{BroadcasterBuilder, ServiceBuilder},
    discovery::{DiscoveryBuilder, DiscoveryEvent},
    net::IpVersion,
};
use std::{
    net::{IpAddr, Ipv4Addr},
    str::FromStr,
};

let (found_tx, found_rx) = std::sync::mpsc::sync_channel(0);

let broadcaster = BroadcasterBuilder::new()
    .loopback()
    .add_service(
        ServiceBuilder::new("_searchlight._udp.local.", "HELLO-WORLD", 1234)
            .unwrap()
            .add_ip_address(IpAddr::V4(Ipv4Addr::from_str("192.168.1.69").unwrap()))
            .add_txt_truncated("key=value")
            .add_txt_truncated("key2=value2")
            .build()
            .unwrap(),
    )
    .build(IpVersion::V4)
    .unwrap()
    .run_in_background();

let discovery = DiscoveryBuilder::new()
    .loopback()
    .service("_searchlight._udp.local.")
    .unwrap()
    .build(IpVersion::V4)
    .unwrap()
    .run_in_background(move |event| {
        if let DiscoveryEvent::ResponderFound(responder) = event {
            found_tx.try_send(responder).ok();
        }
    });

println!("Waiting for discovery to find responder...");

println!("{:#?}", found_rx.recv().unwrap());

println!("Shutting down...");

broadcaster.shutdown().unwrap();
discovery.shutdown().unwrap();

println!("Done!");

贡献

除非您明确声明,否则您有意提交以包含在您的工作中的任何贡献,根据MIT许可证定义,将双重许可如上,不附加任何额外条款或条件。

依赖项

~8–21MB
~259K SLoC