Windows下mDNS查询API—DnsStartMulticastQuery/DnsStopMulticastQuery的使用
背景及问题:
目前很多局域网设备通过mNDS协议实现互联,IP地址为自动IP段-169.254.x.x,有时候设备厂家提供的API需要通过知晓局域网中的IP地址/设备名,才能连接该设备。这样要求每个软件必须配置设备名或者启动时遍历所有IP(6w+),不是很方便,这时候可以通过mDNS查询,自动拿到设备名,再进行连接,相对便捷,高效。
解决办法:
在Win10 版本中,windows提供了mDNS查询的API-DnsStartMulticastQuery,官方定义如下:
其中PMDNS_QUERY_REQUEST定义为
其中各字段定义如下:
Version | 必须 DNS_QUERY_REQUEST_VERSION1结构版本 |
ulRefCount | 保留。 请勿使用。 |
Query | 表示要通过 mDNS 查询的名称的字符串。 |
QueryType | 一个值,该值表示要查询的记录的类型。有关可能的值,请参阅DNS_RECORD_TYPE 。 |
QueryOptions | 一个表示查询选项的 值。 DNS_QUERY_STANDARD 是唯一支持的值。 |
InterfaceIndex | 一个 值,该值包含要播发服务的接口索引。 如果 InterfaceIndex 为 0,则将考虑所有接口。 |
pQueryCallback |
指向类型为 MDNS_QUERY_CALLBACK) 的函数 (的指针,该函数表示每当 mDNS 结果可用时要异步调用的回调 |
pQueryContext | 指向用户上下文的指针。 |
fAnswerReceived | 保留。 请勿使用。 |
ulResendCount | 保留。 请勿使用 |
需要注意的是:
Query是所查询的字符串, pQueryCallback是查询等到回复的回调函数,定义如下:
其中PDNS_QUERY_RESULT含有查询结果DNS_RECORD ,对该结构体进行解析可以得到结果。拿到结果之后需要使用 DnsRecordListFree 函数释放空间。
示例程序:
#include<Windows.h> #include"mdns.h" #pragma comment(lib, "dnsapi.lib") VOID WINAPI multicastQueryCallback(PVOID pQueryContext, PMDNS_QUERY_HANDLE pQueryHandle, PDNS_QUERY_RESULT pQueryResults) { //pQueryResults为返回结果,对其中的pQueryRecords进行解析 if (pQueryResults->pQueryRecords) { //解析查询结果 // 释放空间 dnsRecordListFree(pQueryResults->pQueryRecords,DnsFreeRecordList) ) } void GetQueryResponse(const wchar_t* queryName) { MDNS_QUERY_HANDLE h1; MDNS_QUERY_REQUEST r1; int context = 222; r1.Version = DNS_QUERY_REQUEST_VERSION1; r1.QueryOptions = DNS_QUERY_STANDARD; r1.QueryType = DNS_TYPE_PTR; r1.Query = queryName; r1.pQueryContext = (PVOID)context; r1.InterfaceIndex = 0; r1.pQueryCallback = multicastQueryCallback; DnsStartMulticastQuery(&r1, &h1); Sleep(100); DnsStopMulticastQuery(&h1); }
需要注意的是:
- 目前该API似乎不是很稳定, Release/X64,双字节字符串(VS字符集需设置为Unicode)平台下使用没有问题
- 此方法为异步查询,如果不使用DnsStopMulticastQuery结束查询,则查询字符串会一直发送,回调函数会被一直调用。
- pQueryContext可以用来存储查询结果(示例中没有使用)
- QueryOptions设为DNS_QUERY_BYPASS_CACHE可以绕过缓存查询,每次查询最新的结果
- 同样的Release/X64平台, DnsQuery API也可以查询,且支持单双字符串,但是DNS_QUERY_BYPASS_CACHE不起作用;同步查询,有1s左右延时