MoonDiscover: DNS-SD browsing and resolution with a portable protocol core
| 场景 | 应用如何使用 | 对应入口 |
|---|---|---|
| 本地开发工具、测试设备的 HTTP 连接 | 订阅约定类型,根据 TXT 筛选实例,使用发现的地址和动态端口 | HTTP 示例 |
| 桌面伴随程序或设备面板 | 维护服务列表,响应新增、解析完成、属性更新、暂不可用和移除 | 连续事件示例 |
| 指定类型的局域网服务盘点 | 浏览一个或多个已知类型,逐行输出 JSON,交给其他程序处理 | CLI browse |
| 故障复现、协议调试与教学 | 保存原始响应和时间,在没有网络时重放同一输入 | --record、replay、离线页面 |
| 自定义运行时或传输适配 | 自行提供时钟、scope、收包与发包逻辑 | 纯核心 Engine 和 Replay |
flowchart LR
P[局域网中已有的服务发布者] <-->|IPv4 mDNS 组播| N[Native Browser]
N <--> E[纯 MoonBit Engine]
E --> S[快照与生命周期事件]
S --> A[你的应用或 CLI]
N --> T[JSONL 录制]
T --> R[离线 Replay]
R --> E| 项目 | Mooncakes 0.1.0 | 0.2.0 / 当前源码 |
|---|---|---|
| 获取方式 | moon add mik1e80/dnssd@0.1.0 | moon add mik1e80/dnssd@0.2.0;源码标签 v0.2.0 |
| 基础能力 | 名称/TXT、有限 codec、缓存、聚合、QM、browse/resolve、Native IPv4、CLI、录制/回放 | 包含基础能力及后续修复 |
| 子类型浏览 | 未实现 | 已实现,专项核心用例通过;Linux Avahi 子类型专项已通过 |
| Known-Answer | 有界单包 | 最多 64 包,增加容量边界、普通 QM 降级与诊断修复;Engine 实际出站字节另由独立测试解析器验收,Linux UDP 与独立测试解析器专项已通过 |
| SRV 候选 | 基础解析 | 按优先级与固定种子权重顺序提供候选列表,详见 选择契约 |
| 输入与 Native 生命周期 | 不包含后续修复 | DTO 缓存接纳原子校验;稳定传输错误代码和关闭行为回归 |
| 容量与异常 PTR flush 修复 | 不包含后续修复 | 已修复并加入回归 |
| 逻辑状态观测 | 不包含后续 Engine.stats() 扩展 | 提供缓存、服务、查询和队列等计数 |
| 验证范围 | Windows 同机独立 zeroconf 发布者、实际注册表消费 | 另有同 Linux VM 的 Avahi 闭环与新增核心回归 |
moon add mik1e80/dnssd@0.2.0| 导入入口 | 用途 | 环境要求 |
|---|---|---|
| mik1e80/dnssd | 名称、TXT、报文、Engine、事件、Replay | 无系统网络依赖;可用于 JS 离线处理 |
| mik1e80/dnssd/src/native | 真实 IPv4 组播 socket 和 Browser | Native 后端及系统 C 工具链 |
git clone https://github.com/mik1e80/MoonDiscover.git moondiscover
cd moondiscover.\scripts\bootstrap.ps1
.\scripts\moon.ps1 run examples/encode_query --target js --frozen
.\scripts\moon.ps1 build cmd/moondiscover --target native --frozen_build/native/debug/build/cmd/moondiscover/moondiscover.execurl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash -s -- '0.10.17+d971670cd'
export PATH="$HOME/.moon/bin:$PATH"
moon update
moon build cmd/moondiscover --target native --frozen
./_build/native/debug/build/cmd/moondiscover/moondiscover --help$cli = '.\_build\native\debug\build\cmd\moondiscover\moondiscover.exe'
& $cli --help
& $cli --version& $cli doctor --interface 192.168.1.10& $cli browse --interface 192.168.1.10 --service _http._tcp.local. --duration-ms 5000& $cli browse --interface 192.168.1.10 --service '_http._tcp.local.,_ipp._tcp.local.' --duration-ms 10000& $cli browse --interface 192.168.1.10 --service _printer._sub._http._tcp.local. --duration-ms 5000& $cli resolve --interface 192.168.1.10 --instance 'Demo._http._tcp.local.' --duration-ms 5000& $cli browse --interface 192.168.1.10 --service _http._tcp.local. --duration-ms 5000 --record trace.jsonl
& $cli replay --file trace.jsonl& $cli replay --file fixtures/live-20261024.jsonl| 字段 | 含义 |
|---|---|
| scope | 当前接口/网络运行范围;同名实例跨 scope 不合并 |
| service_type、instance、instance_label | 基础类型、可逆实例名、用于展示的首标签 |
| target、port、addresses | 当前发现的主机、端口和地址;尚缺的值可能为 null/空数组 |
| txt_entries_hex | 原始 TXT 条目十六进制,保留任意二进制内容 |
| complete、missing | 当前记录链是否完整,以及缺失/冲突项 |
| remaining_ttl_ms | 当前快照的剩余缓存有效期 |
| 事件 | 含义与调用方处理建议 |
|---|---|
| seen | 首次观察实例,可能为部分状态,可先显示在列表中 |
| resolved | 记录链首次完整,可按业务规则选择连接候选 |
| updated | 内容变化,重新评估 TXT、端点或连接候选 |
| unavailable | 已观察实例失去完整性,暂缓依赖完整端点的操作 |
| removed | 不再属于当前观察结果,按原因更新列表 |
| diagnostic | 解析、容量、队列或查询降级信息,查看 code / detail;代码表见诊断目录 |
| resolve_timeout | 指定实例的初次解析超时 |
import {
"mik1e80/dnssd",
}
pkgtype(kind: "executable")fn main raise {
let name = @dnssd.DnsName::from_utf8_labels(["_http", "_tcp", "local"])
let packet = @dnssd.qm_query(name, 12)
let decoded = @dnssd.decode_packet(packet)
println(
"QM PTR query: \{decoded.questions[0].name.to_presentation()} (\{packet.length()} bytes)",
)
}async fn main {
let args = @env.args()
guard args.length() == 3 else {
fail("usage: watch_events <interface-ipv4> <service-type>")
}
let interface = @socket.Addr::parse(args[1] + ":0")
guard !interface.is_ipv6() else { fail("IPv4 required") }
let browser = @native.Browser::open(interface.ip())
defer browser.close()
let _ = browser.browse(@dnssd.DnsName::parse(args[2]))
browser.run(5000, event => {
@stdio.stdout.write(@dnssd.event_json(event).stringify() + "\n")
})
}.\scripts\moon.ps1 run examples/watch_events --target native --frozen -- 192.168.1.10 _http._tcp.local..\scripts\build-replay.ps1
python -m http.server 8000 --bind 127.0.0.1 --directory .tools/browser-replay| 项目 | 当前范围 |
|---|---|
| 发现域 | 显式订阅 _服务._tcp.local. / _服务._udp.local.;main 支持 子类型._sub.基础类型 |
| 真实传输 | 指定本机接口的 IPv4 QM 组播;UDP 5353、目标 224.0.0.251 |
| Native 接纳来源 | 源端口 5353、目标为上述组播地址、入站接口与所选接口一致 |
| 地址记录 | 解码和聚合 A / AAAA;AAAA 不等于 IPv6 组播传输或已验证 IPv6 连接 |
| 支持记录 | PTR、SRV、TXT、A、AAAA;未知 RR 有界保留/跳过,不自动成为业务能力 |
| 缓存 | scope 隔离、TTL、goodbye 一秒宽限、flush 近期分包保护;main 忽略 DNS-SD PTR 异常 flush 位 |
| 冲突记录 | SRV 汇集同范围未过期 RRSet,按优先级与固定种子权重顺序公开候选;替换遵循 cache-flush 宽限。多个冲突 TXT 仍保持未完成 |
| 接口变化 | 接口失效或地址/索引变化报错,不自动迁移到新接口 |
| 异步实现 | 短周期协作轮询,尚未接入系统原生异步 fd 事件源 |
| 资源 | 当前 main 的限制或默认值 |
|---|---|
| DNS 标签/完整名称 | 每标签 1..63 字节;完整 mDNS wire 名称最多 256 字节,含根终止字节 |
| 单 DNS 报文 | 最多 9000 字节;问题和 RR 合计最多 256;单名称解码步数受限 |
| TXT 模型 | 单条最多 255 字节,总 RDATA 最多 65535;可表示不等于能放入一个 UDP 报文 |
| Known-Answer burst | 最多 64 包;首包 255 条、后续每包 256 条,默认最多 16383;另受字节限制 |
| Engine 缓存/服务 | 默认 2048 条记录 / 512 个服务,可配置上限分别为 16384 / 4096 |
| Engine 事件/查询任务 | 默认各 256,可配置上限分别为 16384 / 8192 |
| 订阅/待发问题 | 每个 Engine 最多 64 个订阅;待发问题队列最多 64 项 |
| CLI 录制/回放 | 8 MiB / 10000 帧;Replay 单行最多 32768 字符,时间为不递减的精确 JSON 整数 |
| 浏览器回放页面 | 另有 20000 条输出上限,较大文件优先用 CLI |
| 环境 | 已有证据 | 不能外推的结论 |
|---|---|---|
| Windows Native | 同机独立 python-zeroconf 发布者;发现、动态端口 HTTP、TXT 更新、goodbye、取消和回放 | 不等于第二设备互通 |
| Linux Native / main | 同 Ubuntu VM 的 Avahi 守护进程,八项真实 IPv4 组播闭环 | 不回填为 0.1.0 Linux 包验收;不等于跨设备 |
| JS 核心/离线浏览器 | 核心冒烟、独立消费、回放和页面回调 | 不等于浏览器实时局域网扫描 |
| 其他环境 | 部分核心用例曾在 Wasm/Native 运行,详见验证记录 | 没有全后端验收,不承诺 macOS/Bonjour 或所有设备兼容 |
| 现象 | 建议检查 |
|---|---|
| doctor 失败 | 接口是否为本机当前 IPv4、接口是否可用;检查错误和共享 5353 绑定条件 |
| doctor 成功但没有服务 | 是否有对应发布者、类型/网卡是否正确、防火墙/VPN/访客网络/AP 隔离是否阻断组播,适当延长观察窗口 |
| 只有 seen 或 complete=false | 查看 missing,等待 SRV/TXT/地址,检查冲突诊断;不要连接空端点 |
| 其他工具可见,本项目不可见 | 核对组播/单播、接口、发现域和类型是否受支持;录制受控报文后分析 |
| 中文名或实例里有点 | 用 DnsName::from_utf8_labels / from_labels 保留边界;CLI 使用 DNS presentation 转义 |
| TXT 看起来像十六进制 | txt_entries_hex 保存原始字节;按业务协议解码,不强制将所有值解释成 UTF-8 |
| event_overflow | 加快事件消费,从 snapshots() 重建列表,按需要调整容量 |
| 安装 0.1.0 后没有 main 的 API | 核对版本表和实际依赖,不把开发文档的新能力当作发布包能力 |
.\scripts\moon.ps1 test src/wire/known_answer_limits_test.mbt src/engine/known_answer_fallback_test.mbt src/engine/query_invalid_wbtest.mbt src/replay/replay_test.mbt --target js --frozen --filter 'smoke:*'| 文档 | 内容 |
|---|---|
| 支持范围 / 协议矩阵 | 按版本、协议、环境划分能力 |
| 验证记录 / 开发指南 | 测过什么、复现方法、轻量开发 |
| 模型说明 | 原始字节、快照、时间、生命周期 |
| HTTP 示例 / 事件示例 | 真实调用方式 |
| 浏览器离线回放 | 加载、查看、复现 JSONL |
| 性能记录 / 生态对比 | 测量范围与项目定位 |
| 当前进度 / 后续路线 | 已完成与仍待验收的工作 |
| 变更记录 / 0.1.0 发布说明 | 版本差异及发布依据 |
| 项目说明 / 参赛准备状态 | 项目现状、准备材料与待核实事项 |
Install
Download zipMoonDiscover: DNS-SD browsing and resolution with a portable protocol core