mDNS(Multicast DNS)——From Apple
https://support.apple.com/kb/TA20999?locale=zh_CN&viewlocale=en_US
Multicast DNS, one of the features incorporated in Bonjour, which was introduced in Mac OS X 10.2.
Bonjour的一个新特性,在Mac OS X10.2后引入,以前叫作Rendezvous
Multicast DNS is one of the features of Bonjour (formerly "Rendezvous"), included in Mac OS X 10.2 or later. It allows you to connect via Internet protocol (IP) to other computers on a local network by name, rather than a numbered address.
允许通过IP协议,在同一网段,利用名字连接,而不是数字IP地址
On the Internet at large, a DNS name such as "www.apple.com" must be resolved to a numbered IP address before your computer can connect to the server. A domain name system (DNS) server normally provides this name resolution service for your computer. Your Internet service provider or network administrator normally provides a local DNS server for your use.
在global网络里,DNS server提供了地址解析服务。
Conversely, Multicast DNS allows computers on a local network, such as home or small office, to look up each others' names and addresses without a DNS server. Each computer knows its own name and responds to requests for that name automatically via IP multicast.
mDNS是相对于本地网络来说的,通过主机名字,利用IP多播,发送请求。
Anywhere that a you could normally use a DNS name such as "www.apple.com", you could instead enter the Bonjour name of the computer on the local network. To indicate that the name should be looked up using local multicast instead of a standard DNS query, all Bonjour host names end with the extension ".local." This helps avoid confusion between names that are local Bonjour computer names ("mycomputer.local") and globally unique DNS names ("www.apple.com").
为了避免冲突,本地bonjour用.local后缀,区分于www.xxx.com。
If you use local Bonjour computer names frequently and do not want to explicitly type ".local." every time, then this extension may be added to the Search Domains field in the Network preference pane. Click the TCP/IP tab in the Network preference pane to access the Search Domains field. Be sure you have selected the correct network port from the Show menu.
如果你要默认使用.local,在网络里面配置设置,并设置对端口。
For more information about Multicast DNS in general, see the Multicast DNS website (http://www.multicastdns.org/).
Note: If you have set up a private DNS server that resolves names in the .local domain, computers using Mac OS X 10.2 will not use the DNS server to resolve these names. This may result in unexpected failures to connect to hostnames defined by your server. You should use a different domain, such as .home, .office, or .lan for DNS on private networks.
注意,如果你之前配置过本地DNS server,请用与.local不同的后缀。
Multicast DNS——From multicastdns.org
Multicast DNS is a way of using familiar DNS programming interfaces, packet formats and operating semantics, in a small network where no conventional DNS server has been installed.
mDNS是一种在本地网络,没有DNS服务器的情况下,使用DNS编程接口,包格式,操作语义的一种方式。(Any idea?
Multicast DNS is a joint effort by participants of the IETF Zero Configuration Networking (zeroconf) and DNS Extensions (dnsext) working groups. The requirements are driven by the Zeroconf working group; the implementation details are a chartered work item for the DNSEXT group. Most of the people working on mDNS are active participants of both working groups.
mDNS的贡献者。
While the requirements for Zeroconf name resolution could be met by designing an entirely new protocol, it is better to provide this functionality by making minimal changes to the current standard DNS protocol. This saves application programmers from having to learn new APIs, and saves application programmers from having to write application code two different ways — one way for large configured networks and a different way for small Zeroconf networks. It means that most current applications need no changes at all to work correctly using mDNS in a Zeroconf network. It also means that engineers do not have to learn an entirely new protocol, and current network packet capture tools can already decode and display DNS packets, so they do not have to be updated to understand new packet formats.
为什么不设计一个新的协议?因为为了程序员不用学习新的协议,也不用在目前的DNS代码里面做大改变,目前的抓包软件也能正确解析DNS包。
-----------------------------------------------------
https://www.cnblogs.com/yuweifeng/p/6409182.html
DNS(Domain Name System,域名系统)因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串。通过主机名,最终得到该主机名对应的IP地址的过程叫做域名解析(或主机名解析)。DNS协议运行在UDP协议之上,使用端口号53。在RFC文档中RFC 2181对DNS有规范说明,RFC 2136对DNS的动态更新进行说明,RFC 2308对DNS查询的反向缓存进行说明。
一、mDNS 具体协议规范地址如下 : http://www.ietf.org/rfc/rfc6762.txt
mdns 即多播dns(Multicast DNS),mDNS主要实现了在没有传统DNS服务器的情况下使局域网内的主机实现相互发现和通信,使用的端口为5353,遵从dns协议,使用现有的DNS信息结构、名语法和资源记录类型。并且没有指定新的操作代码或响应代码。
在局域网中,设备和设备之前相互通信需要知道对方的ip地址的,大多数情况,设备的ip不是静态ip地址,而是通过dhcp 协议动态分配的ip 地址,如何设备发现呢,就是要mdns大显身手,例如:现在物联网设备和app之间的通信,要么app通过广播,要么通过组播,发一些特定信息,感兴趣设备应答,实现局域网设备的发现,当然mdns 比这强大的多
1.mDNS 基于 UDP 协议。
组播地址: 组播地址使用的是D类地址,地址范围为:224.0.0.0—239.255.255.255
2.mdns 工作原理简单描述:
mdns 使用组播地址为: 224.0.0.251 (ipv6: FF02::FB) 端口为5353,mdns 是用于局域网内部的,并且主机的域名为.local 结尾,每个进入局域网的主机,如果开启了mDNS服务的话,都会向局域网内的所有主机组播一个消息,我是谁(域名),和我的IP地址是多少。然后其他有mdns服务的主机就会响应,也会告诉你,它是谁(域名),它的IP地址是多少。 当然设备需要服务时,就是使用mdns 查询域名对对应的ip地址,对应的设备收到该报文后同样通过组播方式应答,此时其他主机设备也是可以收到该应答报文,其他主机也会记录域名和ip 以及ttl 等,更新缓存
比如,A主机进入局域网,开启了 mDNS 服务,并向 mDNS 服务注册以下信息:我提供 FTP 服务,我的IP是 192.168.1.101,端口是 21。当B主机进入局域网,并向 B 主机的 mDNS 服务请求,我要找局域网内 FTP 服务器,B主机的 mDNS 就会去局域网内向其他的 mDNS 询问,并且最终告诉你,有一个IP地址为 192.168.1.101,端口号是 21 的主机,也就是 A 主机提供 FTP 服务,所以 B 主机就知道了 A 主机的 IP 地址和端口号了。
大概的原理就是这样子,mDNS提供的服务要远远多于这个,当然服务多但并不复杂。
3.mDNSResponder与Bonjour的关系:
The mDNSResponder project is a component of Bonjour,
Apple's ease-of-use IP networking initiative:
<http://developer.apple.com/bonjour/>
Bonjour是法语中的Hello之意。它是Apple公司为基于组播域名服务(multicast DNS)的开放性零配置网络标准所起的名字。使用Bonjour的设备在网络中自动组播它们自己的服务信息并监听其它设备的服务信息。设备之间就像在打招呼,这也是该技术命名为Bonjour的原因。Bonjour使得局域网中的系统和服务即使在没有网络管理员的情况下也很容易被找到。
举一个简单的例子:在局域网中,如果要进行打印服务,必须先知道打印服务器的IP地址。此IP地址一般由IT部门的人负责分配,然后他还得全员发邮件以公示此地址。有了Bonjour以后,打印服务器自己会依据零配置网络标准在局域网内部找到一个可用的IP并注册一个打印服务,名为“print service”之类的。当客户端需要打印服务时,会先搜索网络内部的打印服务器。由于不知道打印服务器的IP地址,客户端只能根据诸如"print service"的名字去查找打印机。在Bonjour的帮助下,客户端最终能找到这台注册了“print service”名字的打印机,并获得它的IP地址以及端口号。
从Bonjour角度来看,该技术主要解决了三个问题:
- Addressing:即为主机分配IP。Bonjour的Addressing处理比较简单,即每个主机在网络内部的地址可选范围内找一个IP,然后查看网络内部是否有其他主机再用。如果该IP没有被分配的话,它将使用此IP。
- Naming:Naming解决的是host名和IP地址的对应关系。Bonjour采用的是Multiple DNS技术,即DNS查询消息将通过UDP组播方式发送。一旦网络内部某个机器发现查询的机器名和自己设置的一样,就回复这条请求。此外,Bonjour还拓展了MDNS的用途,即除了能查找host外,还支持对service的查找。不过,Bonjour的Naming有一个限制,即网络内部不能有重名的host或service。
- Service Discovery:SD基于上面的Naming工作,它使得应用程序能查找到网络内部的服务,并解析该服务对应的IP地址和端口号。应用程序一旦得到服务的IP地址和端口号,就可以直接和该服务建立交互关系。
Bonjour技术在Mac OS以及Itunes、Iphone上都得到了广泛应用。为了进一步推广,Apple通过开源工程mdnsresponder将其开源出来。在Windows平台上,它将生成一个后台程序mdnsresponder。在Android平台上(或者说支持POSIX的Linux平台)它是一个名为mdnsd的程序。不过,不论是mdnsresponder还是mdnsd,应用开发者要做的仅仅是利用Bonjour的API向它们发起服务注册、服务查询和服务解析等请求并接收来自它们的处理结果。
下面我们将介绍Bonjour API中使用最多的三个函数,它们分别是服务注册、服务查询和服务解析。理解这三个函数的功能也是理解MDnsSdListener的基础。
使用Bonjour API必须包含如下的头文件和动态库,并连接到:
#include <dns_sd.h> //必须包含此头文件
libmdnssd.so //链接到此so
Bonjour中,服务注册的API为DNSServiceRegister,原型如图1所示:
图1 DNSServiceRegister原型
该函数的解释如下:
- sdRef:代表一个未初始化的DNSService实体。其类型DNSServiceRef是指针。该参数最终由DNSServiceRegister函数分配内存并初始化。
- flags:表示当网络内部有重名服务时的冲突处理。默认是按顺序修改服务名。例如要注册的服务名为“printer”,当检测到重名冲突时,就可改名为“printer(1)”。
- interfaceIndex:表示该服务输出到主机的哪些网络接口上。值-1表示仅对本机支持,也就是该服务的用在loop接口上。
- name:表示服务名,为空的话就取机器名。
- regtype:服务类型,用字符串表达。Bonjour要求格式为"_服务名._传输协议",例如"_ftp._tcp"。目前传输协议仅支持TCP和UDP。
- domian和host一般都为空。
- port表示该服务的端口。如果为0的话,Bonjour会自动分配一个。
- txtLen以及txtRecord字符串用来描述该服务。一般都设置为空。
- callBack:设置回调函数。该服注册的请求结果都会通过它回调给客户端。
- context:上下文指针,由应用程序设置。
当客户端需要搜索网络内部特定服务时,需要使用DNSServiceBrowser API,其原型如图2所示:
图2 DNSServiceBrowser原型
其中:
- sdref、interfaceIndex、regtype、domain以及context含义与DNSServiceRegister一样。
- flags:在本函数中没有作用。
- callBack:为DNSServiceBrowser处理结果的回调通知接口。
当客户端想获得指定服务的IP和端口号时,需要使用DNSServiceResolve API,其原型如图3所示:
图3 DNSServiceResolve原型
其中:
- name、regtype和domain都从DNSServiceBrowse函数的处理结果中获得。
- callBack用于通知DNSServiceResolve的处理结果。该回调函数将返回服务的IP地址和端口号。
如果需要了解Bonjour安卓中的使用方法及原理,请阅读该部分的原文: http://blog.csdn.net/innost/article/details/8629139
4.Linux中的使用方法:
附件提供了mDNS的源码,分析源码我们就可以知道如何编译安装以及如何使用:
在mDNSResponder-107.5\mDNSPosix目录中的Responder.c文件中的main我们可以看到
调用了PrintUsage函数中提供了用法说明:
根据上面的描述,使用shell命令调用的例子:
//mDNSResponderPosix来源于bonjour,服务注册
sprintf(buf, "mDNSResponderPosix -n %s -t _ipc_http._tcp. " "-d local. -p 5959 &", gpCC->ccArg.pDevId);//-n 服务名,-t 服务类型,-d域名,-p端口号,
system(buf);//后台运行
再看main函数中执行过程:
初始化后调用了RegisterOurServices函数把要提供的服务注册进去(追踪下去就可以看到最终调用了mdnscore.c中的mDNS_RegisterService函数):
mDNSResponder介绍与移植
https://blog.csdn.net/yuangc/article/details/101676976
mDNSResponder是苹果的Bonjour项目的一部分。 Bonjour是法语“你好”的意思。
Bonjour软件源自正IETF零配置网络工作。零配置工作有三个要求:
1.分配IP地址(即使没有分配DHCP服务器的IP地址)
2.提供名称到地址的转换(即使没有DNS服务器)
3.在网络上发现相关的网络服务(同样没有其他的基础协议支持)
也就是说不需要以上DNS等相关的服务的支持,通过零配置直接完成这些任务。厉害吧?
对于1,通过自分配的本地链接地址实现。
对于2,通过多播(mDNS)发送类似DNS的查询来满足。
对于3,通过DNS Service Dicsovery(DNS-SD)满足。
自分配的本地链接地址自1998年, 在Windows '98和Mac OS 8.5中shouci出现。当然在其他平台上也支持。
mDNSResponder
mDNSResponder项目实现了上面的2和3。其中,m是Multicast缩写。即使用户没有设置传统的DNS服务器,用户也能够使用服务名称(比如www.sohu.com),而不是点分十进制IP地址(suhu的IP是122.13.86.100)来标识主机。它还为用户无需事先了解服务的细节,就可以发现网络上正在播发什么服务,也无需配置机器。
选择名称“ mDNS”,是因为该协议类似于常规DNS。不过,mDNS和DNS的主要区别在于:
mDNS查询是通过多播发送到所有本地主机,而不是通过单播发送到特定的已知服务器。本地链接上的每个主机都运行一个mDNSResponder,该DNS响应器不断监听那些多播查询,如果mDNSResponder接收到自己关注的查询,则做出响应。
mDNS协议使用与单播DNS相同的数据包格式,相同的名称结构和相同的DNS记录类型。这部分的主要区别在于:
1.查询被发送到不同的UDP端口(5353,而不是53),并通过多播发送到地址224.0.0.251。
2.所有“ mDNS”名称都以“ .local”结尾。当用户键入“ yourcomputer.local”时。进入他们的Web浏览器时,就会显示“ .local”。.local告诉主机OS应该使用本地多播查找该名称,而不是通过将该名称发送给互联网的DNS服务。这有助于用户区分特定名称是广域网的(例如“ www.apple.com”)还是仅仅在本地(例如“ yourcomputer.local”)。
mDNSResponder源代码
由于Apple认为公开这部分代码比较好,可以供其他开发人员使用。这部分代码可以兼容不同种类的OS。
典型的mDNS程序包含三个组件:
在这里插入图片描述
“ mDNS Core”层在所有应用程序和所有操作系统都是相同的。
“Platform Support”层提供了特定于每个平台的必要支持例程,例如,调用哪个例程以发送UDP数据包,调用哪个例程以加入多播组等等。
“Application”层可以执行特定应用程序想要执行的任何操作。 它调用“ mDNS Core”层提供的例程来执行所需的功能:
*发布服务,
*浏览特定服务类型的命名实例
*将命名实例解析为特定的IP地址和端口号,
移植
Apple当前为Mac OS 9,Mac OS X,Microsoft Windows,VxWorks以及POSIX平台(例如Linux,Solaris,FreeBSD等)提供“平台支持”层。
注意,OS X已经提供了mDNS 对应的系统调用,因此不需要移植这份代码。
如果每个应用程序将mDNSResponder代码链接到应用内部中,那么最终将遇到如下图所示的情况:
在这里插入图片描述
这样效率不高。其实OS X提供了一种通用的系统服务,可以通过“ /usr/include/dns_sd.h”API对其进行访问就ok了。所以实际上每个应用都访问daemon程序,只用一份就好了:
在这里插入图片描述
如果希望在内部使用,不用第三方的服务,可以参照上图来移植。在 mDNSPosix目录有具体的说明。
在旧版本的编译器移植
有的编译器太旧,不支持双斜杠的注释://,那么就要手动改了。比如:
打开BBEdit:
打开 “Find” 对话选择 “Use Grep”
搜索 : ([^:])//(.*)
替换: \1/*\2 */
将mDNSResponder代码拖到多文件搜索界面
选择"Replace All" 替换所有。
对于面向更多命令行,可以进入代码目录,执行下面的命令:
find mDNSResponder ( -name *.c* -or -name *.h ) -exec sed -i .orig -e ‘s,^//(.),/\1 /,’ -e '//*/!s,([^:])//(.),\1/*\2 */,’ {} ;
mDNSPosix
mDNSPosix是Apple的mDNS到Posix平台的移植。
目录:
mDNSCore-包含核心mDNS代码的目录。该代码是用纯ANSI C编写的,并被证明具有很高的可移植性。每个平台都需要此核心协议引擎代码。
mDNSShared-一个包含有用代码的目录,该代码不是主协议引擎本身的核心。
mDNSPosix-特定于Posix平台的文件:Linux,Solaris,FreeBSD,NetBSD,OpenBSD等。
Clients -客户端代码示例,显示如何将API用于守护程序提供的服务。
编译代码
在mDNSPosix目录,make对应的系统名称:
make os=linux
之后在mDNSPosix/build/prod目录会生成下面的文件:
.
├── libdns_sd.so
├── libnss_mdns-0.2.so
├── mDNSClientPosix
├── mdnsd
├── mDNSIdentify
├── mDNSNetMonitor
├── mDNSProxyResponderPosix
└── mDNSResponderPosix
通用平台使用(例如在台式计算机上):
-mdnsd
-libmdns
-nss_mdns(有关nss_mdns,请参见nss_ReadMe.txt)
专用平台使用:
-mDNSClientPosix
-mDNSResponderPosix
-mDNSProxyResponderPosix
测试工具:
-dns-sd命令行工具(来自“Client”文件夹)
-mDNSNetMonitor
-mDNSIdentify
运行说明
在这里插入图片描述
mdnsd分为三个部分。
o mDNSCore是主要的协议引擎
o mDNSPosix.c提供了在Posix OS上的对应移植
o uds_daemon.c将Unix域套接字接口,导出到mDNSCore提供的服务
客户端应用程序与libmdns链接,libmdns实现dns_sd.h头文件中定义的功能,并实现IPC协议,该协议用于通过Unix Domain Socket接口与守护程序进行通信。
严格来说,nss_mdns只是mdnsd的另一个客户端,就像其他任何客户端一样与libmdns链接。只不过它在多播DNS的正常运行中起着核心作用,因此它与其他必要的系统支持组件一起编译安装。
小型嵌入式系统客户端
有时候需要在极小的系统下支持mdns,但是系统资源有限,这时候可以取消uds_daemon和libmdns层。直接调用core的接口。下面是一些简要的说明,如果没有这样的需求就直接跳过不用看了。
在这里插入图片描述
不过,这样工作量会比较大。
1.应用程序调用mDNS_Init,后者调用平台(mDNSPlatformInit)。
mDNSPlatformInit获取接口列表(get_ifi_info),并向核心注册每个接口(mDNS_RegisterInterface)。它还为每个接口创建一个多播套接字(SetupSocket)。
3.然后,应用程序反复调用select()来处理文件描述符事件。在每次调用select()之前,应用程序都会调用mDNSPosixGetFDSet()来给mDNSPosix.ca机会将其自己的文件描述符添加到集合中,然后在select()返回之后, 调用mDNSPosixProcessFDSet()来使 接收并处理数据包。
4.当核心需要发送UDP数据包时, 调用mDNSPlatformSendUDP。该例程查找与内核请求的源地址相对应的接口,并使用为该接口创建的UDP套接字发送数据报。如果套接字是流发送侧控制的,则丢弃数据包。
5.当SocketDataReady运行时,它使用复杂的例程“ recvfrom_flags”来接收数据包。
另外,如果决定在平台中使用线程,注意要实现mDNSPlatformLock()和mDNSPlatformUnlock()调用。