局域网中服务(设备)发现
- 早期用于发现局域网中的打印机,摄像头
- 局域网中发现显示器,tv,和手机等移动设备
- 局域网iot物联场景下用来发现物联设备
下图是一个简单的网络,发现的意思就是让PC A和PC B知道其中一方或双方的存在。
发现服务和发现设备的区别
- 发现服务:是指发现某个设备的某个端口进程服务程序,一个设备可以有多个服务。
- 发现服务不就可以使用服务确认设备了?
注意:发现服务不代表要使用服务,这是两个不同的功能
基础网络知识
这部分会讲到单播,广播,组播(多播),熟悉的同学可以跳过这部分,直接浏览下文的:
- [广泛使用的服务发现协议Zeroconf](# 广泛使用的服务发现协议Zeroconf)
单播
两个在局域网络中, PC A中的程序A 想要在计算机网络中将数据包发送给PC B 中的程序B(两个程序进行确定唯一目的地的数据交互)可以采用单播的形式,通常的网络协议大多是单播,例如常见的TCP协议。
所以在进行单播前,程序A必须得知道程序B的ip + prot,怎么办?
-
提前知道
- 如果该局域网中,PC B的IP是静态的,那就可以提前在程序A中写入
-
询问
- 如果PC B的ip是动态的,那就可以在局域网中询问。
广播
一种方式是广播,广播是指某个程序将数据包发送个这个网络中的所有设备。程序A的询问过程如下图:
如果PC A 发出的广播是带有自己的地址信息的,那当PC B的程序收到了广播后,将会使用单播的形式告知PC A自己的地址,反之采用广播,这里我就只给出单播的示意图:
组播(多播)
另一种询问的方式是组播。
从上文可以看到广播会向局域网中所有的设备都发送数据包,而PC C根本不想收到这个数据包,因为它根本就不是某一服务类型的提供方,收到数据也不必回复。所以PC A到PC C的通信就浪费了资源(占用通信链路,以及多一次的数据拷贝),当多设备多还可能会造成广播风暴(广播数据包充满了整个网络,导致其他数据包无法被转发)。
根本原因:没有人去分类各个服务程序所属的类型,也就是没有分组。如果分好了组,负责转发的二三层网关设备就可以将数据包转发给组内的成员,不属于组成员的设备将不会受到数据包。
因此在三层设备中就出现了IGMP(Internet Group Management Protocol)互联网组管理协议,而二层网关设备用的是IGMP Snooping (Internet Group Management Protocol Snooping)两者相辅相成完成组播功能。
ps:当然还有其他的组播协议,例如三层的PIM、MSDP、MBGP;二层的组播VLAN 等。(是的,我都不了解?)
IGMP简单介绍
IGMP是Internet Group Management Protocol的简称,又被称为互联网组管理协议,是TCP/IP协议族中负责IPv4组播成员管理的协议。IGMP用来在接收者主机和与其直接相邻的组播路由器之间建立和维护组播组成员关系。IGMP通过在接收者主机和组播路由器之间交互IGMP报文实现组成员管理功能,IGMP报文封装在IP报文中。组播路由器通过选举产生,会动态变化。
IP组播通信的特点是报文从一个源发出,被转发到一组特定的接收者。但在组播通信模型中,发送者不关注接收者的位置信息,只是将数据发送到约定的目的组播地址。要使组播报文最终能够到达接收者,需要某种机制使连接接收者网段的组播路由器能够了解到该网段存在哪些组播接收者,同时保证接收者可以加入相应的组播组中。IGMP就是用来在接收者主机和与其所在网段直接相邻的组播路由器之间建立、维护组播组成员关系的协议。
来源:wiki
Zeroconf协议主要服务于三层,所以这里就只讲三层网关设备使用IGMP v3(我这里是v3)管理分组的简单过程:
主机加入组
主机主动告知网络中的组播路由器要加入某个组,这个时候组播路由器就知道网络内有224.0.0.251组的成员,会建立组的转发表,如下图所示:
注意:图中所有的地址都是我示例用,不一定符合标准
当有其他主机发送广播的目的地址为224.0.0.251时,组播路由器会将这个数据包转发给组内的其他成员,所以源主机根本不关注目的主机是谁。
组播路由器自己发现组
组播路由器(在IGMP中称为查询器)会周期的向局域网发送广播查询有哪些组存在,过程如下图所示:
可以看到所有主机都会收到来自组播路由器的查询数据包,假设PC A 、PC B、PC C都是224.0.251这个组的成员,他们在收到后会启动随机计时器,谁先计时结束就发出组播,如上图中的PC B主机。组播路由器收到后就知道网络中还有这个组的成员,会继续维持该组的转发,而其他主机收到后就不会再重复发出回复,这是通过抑制的方式,达到减少对网络占用的目的。
ps:在IGMPV3的协议中,组成员的离开是会主动告知组播路由器的,不必等待查询器查询。
发出组播消息
PC A向发送组播给组播路由器,由组播路由器进行转发给组播成员,如下图所示,其中源地址为自己的主机地址,目的地址为组播地址,但都是会由组播路由器接收并转发给组内成员:
自己实现一个设备发现
当了解了单播,广播,组播后,要你写一个局域网内的设备发现功能,其实不难。
暴露自己
例如可以选择组播实现:
- 主机向组播路由器宣告要成为某某组播的成员。注意:可用的组播可用的ip地址范围是有限制的,具体如下:
224.0.0.0~224.0.0.255 | 为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其它地址供路由协议使用; |
---|---|
224.0.1.0~231.255.255.255 233.0.0.0~238.255.255.255 | 是公用组播地址,可以用于Internet; |
232.0.0.0~232.255.255.255 | 为用户可用的组播地址(临时组地址),全网范围内有效; |
239.0.0.0~239.255.255.255 | 为本地管理组播地址,仅在特定的本地范围内有效。 |
- 某个主机发送组播,并带上自己的地址信息
- 组内成员收到组播信息,就能发现对方,拿到地址具体采用哪种方式进行通信,取决于业务场景
等待别人找你
- 主机向组播路由器宣告要成为某某组播的成员。
- 某个主机发送组播,并带上自己的地址信息,询问谁能提供某服务
- 组成员中恰好有人提供该服务,便使用单播或组播回复。
问题
自己实现固然爽,但是存在一些问题:
-
询问的数据包协议怎么定?这里明显是不知道对方的ip,而是知道对方所提供的服务类型,希望通过服务类型找到对方具体ip。
如果只想自己玩,你可以自己定协议,其中包含程序服务类型程序服务名称以及ip地址和端口的声明规范,这样一来找到对方并通信然后使用对方的服务完全不是问题。
但你定的协议在行业内并不通用,即使你定的协议比较漂亮和优雅,想成为行业标准也是比较困难的事情,但可以搞,具体查询RFC相关资料,不小心成为了标准,血赚!
那行业中是否已经有这样的协议?这时候你可能想到DNS服务。
-
假设没有DNS服务怎么办?
dns服务处于应用层,你不能保证网络中有部署dns服务器
-
假设设备的操作系统不支持DHCP怎么办?
DHCP处于应用层
-
技术人员没有主动配置hosts主机表怎么办?
-
一些操作系统根本不支持hosts配置怎么办?
广泛使用的服务发现协议Zeroconf
局域网内的比较通用的服务发现协议是Zeroconf
Zeroconf全称为Zero configuration networking,中文名则为零配置网络服务规范。Zeroconf在1999年提出,是一种用于自动生成可用IP地址的网络技术,不需要额外的手动配置和专属的配置服务器。
零配置网络服务,听起来就很牛逼对吧,该协议能实现:
- 自动分配ip
- 提供不需要依赖DNS服务器的服务发现功能
完美解决了前面的问题
Bonjour
将从苹果的Bonjour协议讲(Bonjour是法语:意为沙瓦滴咔),因为Bonjour是Zeroconf最广为人知的一个实现。
Bonjour官网的介绍:
苹果已经将Bonjour开源,在 OS X 和 iOS 中,Bonjour 提供了使用 Foundation、Core Foundation 和 C API 来通告和发现服务的能力。在 OS X 中,Bonjour 还提供了 Java API。在 Windows 和 Linux 等其他平台上,Bonjour 提供了 C API。
来源:Bonjour官网
这意味着大多不同操作系统的设备,通过接入Bonjour协议就可以十分方便的局域网中发现对方。
Bonjour协议提供的API
Bonjour将底层的具体实现屏蔽,提供给上层应用简单的API,使用者从三种基础通信变成了简单的函数调用:
- 通告服务API:用来宣告自己提供哪些服务
- 发现服务API:用来查询局域网内是否有它所需要的服务
- 解析服务API:通过发现服务API获取服务实例,进一步解析服务提供的信息
Bonjour实现方式
Bonjour是通过三种协议实现的:Link-Local Address协议、Multicast Dns协议、DNS-SD(dns-based service discovery)协议,下面会一个一个介绍。
Link-Local Address协议
先讲解决ip问题的协议:Link-Local Address
链路本地地址,又称链接本地地址是计算机网络中一类特殊的地址, 它仅供于在网段,或广播域中的主机相互通信使用。这类主机通常不需要外部互联网服务,仅有主机间相互通讯的需求。 IPv4链路本地地址定义在169.254.0.0/16地址块。 IPv6定义在fe80::/10地址块。
来源:wiki
简而言之就是自己在某段地址范围(169.254.0.0 ~ 169.254.255.255)选一个进行探测,如果局域网中是有占用则重新选,没有则使用。过程如下图:
- 使用ARP协议查询选中ip对应的mac地址,查询三次
- 如果没有查到,则宣告自己要用这个ip,同时把自己的mac地址带上
3.假设查到并得到单播回复,则重复第1、2步
小结
这样一来就实现了ip的分配,效率感觉较低,但是在局域网应是够用的。
Multicast Dns协议
接下来就是Zeroconf的第二个功能:提供不需要依赖DNS服务器的服务发现功能。
该功能由两个协议实现,首先要提到的是:Multicast Dns (缩写MDNS),它通过组播的方式实现局域网内的DNS,数据包格式和DNS基本相同,但增加了资源记录数据,这里先不讲这个资源记录,后面再详细讲。
所以该协议可以用来建立服务名与ip的关系。服务名是以域名的形式定义,MDNS的顶级域名为.local
看看具体流程:
上面有讲到发送组播的流程,想要发送某个组播,需要知道 Multicast Dns 的组播地址:224.0.0.251,端口:5353,
- 提供服务的主机将自己提供服务的域名 “xxx.local” 与ip等信息通过组播发送出去查询,发送的是查询报文,重复3次。过程如下:
-
如果没有其他主机回复,则会再发一次组播,宣告自己提供服务,ip是xxxx,端口是xxx,发送的是响应报文,其他机器收到后会更新本地的dns缓存
这里注意一个重点:使用MDNS协议的主机会维护一个映射表,并且只会处理顶级域名为.local的数据,并且存储区域也是和DNS服务分开的
3.如果有人回复该域名已经有映射关系,会改域名重新进行第1、2步,回复是也是组播的形式,而不是单播。
这里有一个比较有意思的点,MDNS采用组播回复,主机可以查询本地缓存,只需要有一台计时器超时的主机回复即可,就可以实现上文组播知识中提到的回复抑制功能,减少对网络的占用。
小结
这样一来就完成了服务域名与ip的映射,这里有个利用缓存实现IGMP协议的回复抑制的功能,相信在以后遇到一对多查询的通信时,我会考虑使用这种方式避免重复的通信。
目录服务器
Multicast DNS协议将服务域名与ip建立映射关系,但是我们知道一个主机可以提供多种服务,同时服务也可以再一次分类,例如类型为打印的服务有很多个,已经开始有树的数据结构出现了,那该如何高效的查找服务呢?
首先要了解的一个概念叫:目录服务器
目录服务(英语:Directory service)是一个储存、组织和提供信息访问服务的软件系统,在软件工程中,一个目录是指一组名字和值的映射。它允许根据一个给出的名字来查找对应的值,与词典相似。像词典中每一个词也许会有多个词义,在一个目录中,一个名字也许会与多个不同的信息相关联。类似地,就像一个词会有多个不同的发音和多个不同的词义,目录中的一个名字可能会有多个不同类型的值。
目录也许只提供范围非常小的节点类型和数值类型,也可能对任意的或可扩展的一组类型提供支持。在一个电话目录中,节点就是姓名而数值项就是电话号码。在DNS中,节点是域名而数值项是IP地址(还有别名,邮件服务器名等等)。在一个网络操作系统的目录中,节点是那些由操作系统所管理的资源,包括用户、计算机、打印机和其它共享资源。互联网问世以来,有许多目录服务得到应用,但是本文主要关注那些源自X.500的目录服务
来源:wiki
使用目录服务器还得配置它,不符合Zeroconf协议。
DNS-SD(dns-based service discovery)协议
DNS-SD (dns-based service discovery) 使用MDNS的资源记录功能中的三种类型存储去建立服务类型、服务实例、服务实例ip+prot的映射关系,以达到快速发现服务的目的。简而言之:该协议定义了三种类型存储的数据格式,相当于建立通讯录,以及通讯录里面的数据格式,以便于发现服务。
你可能会疑问为什么MDNS不直接实现这个功能?
我个人认为:MDNS的功能定义是明确的,用于实现无DNS服务的域名解析。服务发现不属于他该有的功能,该功能属于某种业务场景,所以莎娃迪卡将DNS-SD与MDNS结合起来,实现服务发现。
DNS-SD对MDNS数据段的使用方式如下:
- PTR记录:将服务名称与服务类型建立映射关系
- SRV记录:将服务与ip地址和端口号建立映射关系
- TXT记录:服务所附带的其他数据
下面就看看DNS-SD的工作过程:
查询及宣告服务
1.查询服务
发送的是查询报文,也是查询三次,DNS-SD基于MDNS将服务域名与ip和prot的信息放在了srv区域:
抓包的数据可以看到是200多毫秒会发送一次查询,查三次:
报文数据如下:
2.宣告服务
如果没有其他主机回复则会再使用组播,宣告自己提供服务,发布的是PTR响应报文:
抓包的数据可以看到这时是PTR类型的响应报文:
回复的报文数据,可以看到有标志位:Cache flush : true 意思是更新缓存,当然PTR可能是前面就存过了所以不需要更新。
- 服务冲突
如果有收到回复存在冲突,将服务域名更改重复第1、2步。
发现服务
- 主机定期通过组播查询某个类型的服务列表,发送的查询依旧是PTR,因为一开始查的都是某个服务类型,进而获得某个服务类型的服务列表:
报文内容,注意,answers可能会有多个服务,我只搞了一个服务而已。:
这里你可能会好奇,为啥查询和回复是一起的?这还是上文提到的回复抑制功能,组播会先到组播路由器,而组播路由器中有缓存记录,组播路由器就直接回复了结果。
解析服务
同时是否注意到这里没有ip和prot?还记得上文说的三个存储数据段吗?这里是PTR 查询,ip和服务域名的映射关系在SRV数据段哦!
所以还需要进一步对服务的解析。
我的流程为:
- 查询的设备ip为:172.17.234.158,通过组播查询并解析具体服务的内容,可以看到目的地址是224.0.0.251。
- 组成员172.17.224.57收到查询,通过单播回复,可以看到数据区有PTR、SRV、TXT数据,172.17.234.158只需要自己取数据就完成了服务信息的解析。
总结
可以看到各个协议的职责十分清晰,粒度很细,Bonjour将这些协议组合后同样的也专注于三个功能,不提供给上层发现后继续通信的接口,即使它有能力提供单播接口,毕竟它自己也用到,但既然上层已经有了ip+prot,怎么通信就是使用方的事情了。
本次最大的收获其实是:查询抑制和回复抑制这两个功能,太??了。
注:本文所有协议的内容都是看网上资料和RFC文档再结合抓包数据分析的,要是我说的有误可以指出,要是不懂或者不认可可以自行查看RFC文档,上文都有给出协议对应的RFC地址。