该库提供了一个仅标头的跨平台 mDNS 和 C 语言的 DNS-DS 库。最新的源代码始终可在
https://github.com/mjansson/mdns
该图书馆属于公共领域;您可以重新分发它和/或修改它,没有任何限制。
由 Mattias Jansson (@maniccoder) 创建
用于讨论的 Discord 服务器 https://discord.gg/M8BwTQrt6c
该库进行 DNS-SD 发现和服务以及一次性单记录 mDNS 查询和响应。库没有完成内存分配,所有使用的缓冲区都必须由调用者传入。可以使用用户数据不透明指针传递用于处理的自定义数据。
mdns.c
测试可执行文件演示了所有功能的使用,包括发现、查询和服务响应。这里的文档故意稀疏,示例代码有很好的文档记录,并且应该提供所有详细信息。
用于 mDNS 通信的套接字可以由库使用mdns_socket_open_ipv4
或mdns_socket_open_ipv6
打开,也可以通过使用mdns_socket_setup_ipv4
或mdns_socket_setup_ipv6
初始化现有套接字来打开。套接字打开/设置函数将使用多播成员身份(包括环回)初始化套接字并设置为非阻塞模式。
调用mdns_socket_close
关闭使用mdns_socket_open_ipv4
或mdns_socket_open_ipv6
打开的套接字。
要打开/设置套接字以进行一次性查询,您可以传递一个空指针套接字地址,或将传递的套接字地址中的端口设置为 0。这将根据 RFC 的要求将套接字绑定到随机的临时本地 UDP 端口一次性查询。进行一次性查询时,不应绑定到端口 5353(有关详细信息,请参阅 RFC)。,
要打开/设置服务的套接字,响应传入的查询,您需要传入套接字地址结构,并将端口设置为 5353(由标头中的 MDNS_PORT 定义)。您不能选择任何其他端口,否则您将不会收到任何传入查询。
要在默认网络接口上进行发现或查询,您可以在套接字创建/设置函数中传递空指针作为套接字地址。这会将套接字绑定到默认网络接口。否则,您应该枚举可用的接口并将适当的套接字地址传递给创建/设置函数。请参阅mdns.c
中的示例程序,了解针对 IPv4 和 IPv6 执行此操作的示例实现。
如果要对传入查询进行 mDNS 服务响应,则不需要枚举接口来对所有接口进行服务响应,因为套接字从所有接口接收数据。有关为 IPv4 和 IPv6 设置服务套接字的示例,请参阅mdns.c
中的示例程序。
要发送 DNS-SD 服务发现请求,请使用mdns_discovery_send
。这将发送单个多播数据包( _services._dns-sd._udp.local.
的单个 PTR 问题记录)请求单播响应。
要读取发现响应,请使用mdns_discovery_recv
。自上次调用以来收到的所有记录都将通过管道传输到函数调用中提供的回调。条目类型将为MDNS_ENTRYTYPE_ANSWER
、 MDNS_ENTRYTYPE_AUTHORITY
和MDNS_ENTRYTYPE_ADDITIONAL
之一。
要发送单个记录的一次性 mDNS 查询,请使用mdns_query_send
。这将为给定的记录和名称发送单个多播数据包(例如_http._tcp.local.
的 PTR 记录)。您可以选择传入查询的查询 ID,以便稍后过滤响应(即使 RFC 不鼓励这样做),或者传递 0 以完全兼容。该函数返回与此查询关联的查询 ID,如果该查询 ID 非零,则可用于过滤mdns_query_recv
中的响应。如果套接字绑定到端口 5353,则请求多播响应,否则请求单播响应。
要读取查询响应,请使用mdns_query_recv
。自上次调用以来收到的所有记录都将通过管道传输到函数调用中提供的回调。如果query_id
参数非零,则该函数将过滤掉查询 ID 与给定查询 ID 不匹配的任何响应。条目类型将为MDNS_ENTRYTYPE_ANSWER
、 MDNS_ENTRYTYPE_AUTHORITY
和MDNS_ENTRYTYPE_ADDITIONAL
之一。
请注意,为来自临时端口的一次性查询而打开的套接字不会收到任何未经请求的答案(通告),因为这些答案是在端口 5353 上作为多播发送的。
要在同一个数据包中发送多个查询,请使用mdns_multiquery_send
,它接受一个数组以及要查询的服务名称和记录类型的计数。
要侦听传入的 DNS-SD 请求和 mDNS 查询,可以通过在调用套接字打开/设置函数时传递 0 作为套接字地址来在默认接口上打开/设置套接字(套接字将从所有网络接口接收数据)。然后在收到传入数据的通知时调用mdns_socket_listen
,或者通过设置阻塞模式并调用mdns_socket_listen
进行阻塞,直到数据可用并被解析为止。
传递给回调的条目类型将为MDNS_ENTRYTYPE_QUESTION
,记录类型指示要响应的记录。该示例程序响应 SRV、PTR、A 和 AAAA 记录。使用mdns_string_extract
函数获取所请求的服务记录的名称字符串。
如果服务记录名称为_services._dns-sd._udp.local.
您应该使用mdns_discovery_answer
发送您提供的服务 (DNS-SD) 的记录。
如果服务记录名称是您提供的服务,请根据问题中的响应类型标志使用mdns_query_answer_unicast
或mdns_query_answer_multicast
来发送回服务详细信息以响应查询。
有关如何处理给定函数的参数的更多详细信息,请参阅测试可执行文件实现。
如果您提供 mDNS 服务在端口 5353 上侦听和回答查询,我们鼓励您在服务启动时发送公告(作为主动答复)。使用mdns_announce_multicast
在启动时宣布服务的记录,并使用mdns_goodbye_multicast
在终止时宣布服务结束。
mdns.c
文件包含使用该库执行 DNS-SD 和 mDNS 查询的测试可执行实现。编译为可执行文件并运行以查看发现、查询和服务模式的命令行选项。
cl mdns.c /Zi /Fdmdns.pdb /link /out:mdns.exe ws2_32.lib iphlpapi.lib
gcc -o mdns mdns.c
clang -o mdns mdns.c
FetchContent
一起使用或安装并find_package
mdns/20200130
,并find_package
-> https://conan.io/center/mdns/20200130vcpkg install mdns
和find_package