gitbook/物联网开发实战/docs/311616.md
2022-09-03 22:05:03 +08:00

157 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 07 | 零配置组网:设备如何发现彼此?
你好,我是郭朝斌。
不知道你还记不记得,在基础篇的[第2讲](https://time.geekbang.org/column/article/306976)中,我介绍了 Wi-Fi 设备的配网方式比如一键配网技术Smart Config和设备热点配网技术。这些技术已经可以实现**一定程度的自动化**让设备比较方便地连接上Wi-Fi 热点。
同时我也提到了零配置配网方式它试图通过已连接上Wi-Fi热点的设备来实现可信任设备**完全自动化**的配网。
但是,你可不要把**零配置配网**Zero Configuration Provisioning和我们这一讲要谈的**零配置组网**Zero Configuration Networking ZEROCONF混淆了。配网只是第一步因为物联网设备无法方便地输入信息所以相比手机或电脑我们引入了这个额外的操作步骤。
配网成功之后,我们还需要组网,也就是让设备获得一个**自己的IP地址**,同时也知道局域网内的**路由器**Router的IP地址和**DNS**Domain Name System的IP地址等信息。设备自己的IP地址是它在TCP/IP 网络中的唯一标识路由器可以把设备的数据包正确地转发出去而DNS服务器可以帮忙解析出数据包中需要设置的目的地 IP 地址。
> 温馨提示如果你对IP地址和DNS这样的基本概念还不是很熟可以学习[《趣谈网络协议》](https://time.geekbang.org/column/intro/100007101)等课程临时恶补一下。因为这一讲还会涉及不少网络基础知识,对它们有一定了解的话,你的学习效果会更好。
零配置组网就是把这些工作自动化,不需要用户手动去操作,甚至可以让这个设备与网络内的其他设备配合工作。
比如你要为自己的智能家居系统加一个投屏设备它可以外接普通电视或者显示器让你可以将手机或者电脑的屏幕在大屏上显示出来。你希望这个投屏设备接入Wi-Fi热点之后你就可以在手机上直接发现它并且马上开始投屏看自己想看的电影。
这个想法非常美,但是你知道要怎么实现吗?其实并不难,你只要掌握我在这一讲介绍的零配置组网就可以了。
### 设备如何获取 IP 地址?
零配置组网的第一步就是自动分配IP地址。
不知道你以前有没有这样的经验,在学校宿舍或者公司,当你的电脑通过网线连接网络时(现在用网线连网的方式已经不那么常见了),有时电脑桌面右下角会弹出气泡,提示“ IP 地址冲突”。
这是因为你的电脑的IP地址被网络内其他电脑占用了。所以你就需要打开网络配置的界面手动填写一个不同的 IP 地址。对于非专业人士来说,这并不是一个很简单的事情;而且对于特殊的**子网掩码**比如不像“255.255.25**5**.0”这样整齐而是“255.255.25**3**.0”这种),即使有经验的用户,可能也很难确定**子网号**和**主机号**分别是什么。
现在随着Wi-Fi的普及我们连网的时候基本上不会再碰到这种情况了。这一方面是因为每一个 Wi-Fi 热点接入的设备数量是有限的,另一方面则是因为 Wi-Fi 路由器提供了完善的 **IP 地址自动分配功能**
#### DHCP 协议
这个自动分配功能是基于 **DHCP 协议**Dynamic Host Configuration Protocol动态主机配置协议实现的。DHCP 协议的前身是 **BOOTP 协议**Bootstrap Protocol从“Bootstrap”这个名字你就可以看出来它解决的正是一个设备接入 IP 网络后,需要完成的第一件事——获取 IP 地址。
在网络发展的初期,接入网络的设备很少,所以,网络地址的分配和设置都是管理员手动来完成的。你可能想象不到,最初整个互联网的 DNS 数据库都是手动维护和更新的。
但是随着接入网络的设备越来越多而且设备更加便携经常移动来移动去手动的方式就跟不上时代发展了。DHCP 协议正是在这种背景下出现的。
也许你对DHCP的名字很熟悉但我还是想在这里稍微展开一下带你了解它背后的工作原理这样你就能在开发中更好地使用它。
DHCP 使用了服务器-客户端的架构模型。
* 当一个设备你的手机接入网络时它自己就会作为DHCP 客户端,请求网络地址。
* 然后DHCP 服务器家里的Wi-Fi路由器会从地址池中挑选一个IP地址分配给这个设备。
* 当设备不再使用这个 IP 时(你带着手机出门/睡觉飞行模式DHCP 服务器会进行回收,之后再分配给其他有需要的设备(你新买的平板)使用。
DHCP 服务器与设备之间的通信是通过 **UDP 传输协议**完成的。因为 UDP 有一个优势那就是不需要提前建立连接关系。DHCP 服务器的端口号是 67设备的端口号是 68它们一般的交互过程是这样的
![](https://static001.geekbang.org/resource/image/5a/aa/5a1b86b9a70504521010262148e761aa.jpg)
1. **Discover**:设备以广播的方式发送 DHCP Discover 消息,表示需要获取 IP 地址。
2. **Offer**DHCP 服务器收到这个消息后,会发出 DHCP Offer 消息,作为回应。消息中带有 DHCP 服务器为设备分配的 IP 地址,也会包含其自身的 IP 地址。
3. **Request**:设备收到 DHCP Offer 消息后,将会广播一条 DHCP Request 消息,正式向 DHCP 服务器请求这个 IP 地址。
4. **ACK**DHCP 服务器收到 DHCP Request 消息后,会判断服务器 IP 是否和自己的地址一致。如果一致,马上向设备回复 DHCP ACK 消息,并指定好 IP 地址和它的租用期限。
5. **Decline**:设备收到 DHCP ACK 消息后,还会验证一下 IP 地址是否可用。如果地址冲突,就说明不可用,它会发出 DHCP Decline 消息;如果地址不冲突,就可用的,设备将会按照租期使用这个 IP 地址。
6. **Release**:当设备不使用这个 IP 地址时,设备可以通过发送 DHCP Release 消息,来释放它。这样 DHCP 服务器可以重新分配这个 IP 地址。
### 如何让手机自动发现投影仪呢?
借助DHCP协议当你的投屏设备接入Wi-Fi热点后就可以自动获得一个自己的IP地址比如“192.168.1.100”同时还会自动得到路由器Router的IP地址并且完成DNS的IP地址的自动设置。
那接下来,怎么让手机自动发现它,然后直接使用呢?
不知道你留意过没有,如果你新买了一台打印机,当这台打印机连上家里的 Wi-Fi 之后,你在电脑上打印文件时,它会自动显示出来供你选择。这是怎么实现的呢?
分析一下一定是电脑通过某种方式知道有这台设备并且知道它就是打印机可以提供打印服务。打印机被电脑自动识别的这个过程就是借助UPnP协议完成的。
#### UPnP 协议
UPnP 是 Universal Plug and Play 的简称,它要实现的目标就是网络设备的即插即用。
UPnP 由设备寻址、设备发现、设备描述、设备控制、事件通知和基于 HTML 的描述界面六部分构成。其中设备寻址同样是基于我刚介绍过的 DHCP 实现如果网络内没有DHCP服务器UPnP会基于自己的AutoIP方法指定一个IP地址。
从整体看UPnP 是一个**多层协议构成的框架体系**,每一层都以相邻的下层为基础,同时又是相邻上层的基础,直至达到应用层为止。你可以参考下面的图片。
![](https://static001.geekbang.org/resource/image/99/a6/99edd1ea26dcd96eb73c6bb4acde4da6.jpg)
这里我重点介绍一下第三层(从下往上数),它基于 HTTP、HTTPU、HTTPMU协议属于传送协议层。传送的内容都是经过“封装”之后存放在特定的XML文件中的。用于**设备和服务发现**的SSDPSimple Service Discovery Protocol简单服务发现协议协议就是基于XML 传送数据的。
你不要被SSDP协议的名字迷惑了其实它既提供了服务发现的功能也提供了设备发现的功能。我们可以基于 SSDP中的M-SEARCH方法来查询设备然后基于设备的响应获得设备的服务能力的描述信息。同时设备可以通过NOTIFY 方法向网络通知自己的服务能力。
借助UPnP协议你的设备就可以自动被发现和使用了。这个自动化的过程我们通常用一个专有名词来概括也就是零配置组网。梳理一下零配置组网包括三个方面的技术内核
1. 为网络设备自动分配 IP 地址一般涉及DHCP协议和AutoIP方法
2. 自动发现和解析设备主要是基于SSDP协议
3. 自动传播和发现各网络设备提供的服务主要也是基于SSDP协议。
这里我贴一下我家里小米电视盒子的一个 UPnP 的组播包的内容。它每3秒就有一包这样用户体验更好手机或者电脑投屏时更容易发现它。
```
NOTIFY * HTTP/1.1
HOST: 239.255.255.250:1900
CACHE-CONTROL: max-age=66
LOCATION: http://192.168.31.188:49152/description.xml
NT: urn:schemas-upnp-org:service:ConnectionManager:1
NTS: ssdp:alive
SERVER: Linux/3.14.29, UPnP/1.0, Portable SDK for UPnP devices/1.6.13
USN: uuid:F7CA5454-3F48-4390-8009-483842e84c17::urn:schemas-upnp-org:service:ConnectionManager:1
```
你可以发现它是基于HTTP1.1版本,准确地说,是 HTTPMU协议同时你可以看到组播地址端口号是1900。
那为了实现UPnP协议你可以使用哪些开源的代码实现呢
比较流行的开源库是 [libupnp](https://pupnp.sourceforge.io/),小米电视盒子使用的就是这个开源库。另外还有一个选择是[GUPnP项目](https://wiki.gnome.org/Projects/GUPnP)它包括几个不同的子项目比如实现SSDP协议的[GSSDP项目](https://gitlab.gnome.org/GNOME/gssdp/)。
Android平台上有一个开源库[Cling](https://github.com/4thline/cling),它是[4th Line组织](http://fourthline.org/)开发的,你可以参考。现在也只能说“参考”,使用的话,需要谨慎评估,因为它已经不再被维护。
#### mDNS 和 DNS-SD
除了 UPnP 协议,零配置组网还可以使用别的协议标准,比如 mDNS 和 DNS-SD 协议。
说起这两个协议,你可能比较陌生,不过苹果设备的 **AirDrop功能**你可能比较熟悉。AirDrop 是通过 **Bonjour 服务**来发现网络上的其他苹果设备的。这个 Bonjour 服务就是 mDNS 协议和 DNS-SD 协议的具体实现。
**mDNS** Multicast DNS协议允许设备在本地的 DNS 名字空间,设置一个本地的域名。之后被询问的时候,它就通过 UDP 把 IP 地址广播出来,这样其它的设备就可以找到它。你可以简单地把 mDNS 理解为 **DNS 的本地网络版本**
比如,主机 A 是 FTP 服务,它接入网络,并开启了 mDNS 服务,就会向 mDNS 服务注册服务信息:
我提供 FTP 服务我的IP是 192.168.1.101,端口是 21。
当主机 B 接入相同的网络时,如果它向主机 B 的 mDNS 服务请求,需要找局域网内 FTP 服务器,主机 B 的 mDNS 就会在局域网内向其他设备的 mDNS 询问。然后,它就会找到主机 A 的 IP 地址和端口号。
**DNS-SD**DNS Service Discovery 协议,一般是和 mDNS 一起使用的。它使用三种 DNS 协议的记录类型来定义协议内容三个记录分别是PTR 记录、SRV 记录和 TXT 记录。它提供了服务发现的功能,作用类似于上面讲到的 SSDP 协议。
### 小结
总结一下,在这一讲中,我介绍了零配置组网的相关技术。零配置组网是一个技术组合,它要实现的目的是自动化网络设备的初始配置过程,让用户不需要额外的手动操作。
1. 零配置组网的第一步是为网络设备自动分配 IP 地址。DHCP 协议是实现设备寻址的标准技术。它是工作在 UDP 协议之上的应用层协议,提供了完善的 IP 地址请求、IP地址分配和租期管理等功能。如果网络内没有DHCP服务器在UPnP中定义了AutoIP方法设备可以自己分配IP地址。在最新的UPnP规范中 AutoIP 切换到了 Link-Local Addressing规范。这类IP地址一般是“**169.254**.0.0/16”范围的地址。
2. 第二步是自动发现和解析设备的名称。在UPnP中一般是基于SSDP协议另一种替代的方案是 mDNS 协议。
3. 第三步是自动传播和发现各网络设备提供的服务一种选择是采用SSDP协议另一种方案是基于mDNS的DNS-SD协议。
UPnP协议整合了DHCP、AutoIP和SSDP等协议规范提供了一个完整的组网协议框架。UPnP 协议实现的功能非常完善比如基于SOAP 提供了设备控制的能力,但是也引来了很多安全隐患,这个在后面我还会介绍到。
我在这一讲介绍的方法主要关于Wi-Fi设备的。你可能想问那蓝牙和 ZigBee 设备怎么办呢其实关于蓝牙和ZigBee这类设备智能家居厂家一般都会制定自己的私有协议比如小米有**Mibeacon协议**,我会在实战篇再为你介绍。
除了这些私有协议,行业内也有一些标准组织主导的开放协议,比如 **AllJoyn 协议**。它为蓝牙和ZigBee设备提供了设备发现和控制的解决方案。现在AllJoyn 已经和 IoTivity 合并。这个合并是为了在物联网场景下,让零配置组网技术进一步发展。
具体来说因为物联网设备用了很多不同的通信技术所以AllJoyn提供了一个抽象层。它为底层网络协议栈定义了统一的接口使得软件工程师可以相对容易地添加和安装新的网络。
AllJoyn 还采用了一种易于理解的对象模型和远程方法调用RMI机制并且它重新实现了总线协议基于D-BUS规范和扩展D-BUS协议用来支持分布式设备。
因为相对来说AllJoyn协议的行业应用还处于早期我就不展开介绍了有这方面需求或者兴趣的话推荐你可以到它的[官网](https://openconnectivity.org/technology/reference-implementation/alljoyn/)详细了解。
我这里总结了一个思维导图,供你参考。
![](https://static001.geekbang.org/resource/image/6e/7b/6e34893e226d5de8e5a5aa6d5059437b.jpg)
### 思考题
最后,我还是给你留一个思考题吧。
在 DHCP 协议的流程介绍中,我提到当设备收到 DHCP 服务器响应的 DHCP ACK 消息后,并不会立即使用这个 IP 地址,而是要先检测一下 IP 地址是否有效。那设备是通过什么方式检测 IP 地址的有效性的呢?
你可以在留言区和我交流你的思考。同时,也欢迎你将这一讲分享给你的朋友一起交流学习。