<audio id="audio" title="07 | 零配置组网：设备如何发现彼此？" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/a5/dc/a5b4820231d7cae4cefa95eb224a09dc.mp3"></audio>

你好，我是郭朝斌。

不知道你还记不记得，在基础篇的[第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，它们一般的交互过程是这样的：

<img src="https://static001.geekbang.org/resource/image/5a/aa/5a1b86b9a70504521010262148e761aa.jpg" alt="">

1. **Discover**：设备以广播的方式发送 DHCP Discover 消息，表示需要获取 IP 地址。
1. **Offer**：DHCP 服务器收到这个消息后，会发出 DHCP Offer 消息，作为回应。消息中带有 DHCP 服务器为设备分配的 IP 地址，也会包含其自身的 IP 地址。
1. **Request**：设备收到 DHCP Offer 消息后，将会广播一条 DHCP Request 消息，正式向 DHCP 服务器请求这个 IP 地址。
1. **ACK**：DHCP 服务器收到 DHCP Request 消息后，会判断服务器 IP 是否和自己的地址一致。如果一致，马上向设备回复 DHCP ACK 消息，并指定好 IP 地址和它的租用期限。
1. **Decline**：设备收到 DHCP ACK 消息后，还会验证一下 IP 地址是否可用。如果地址冲突，就说明不可用，它会发出 DHCP Decline 消息；如果地址不冲突，就可用的，设备将会按照租期使用这个 IP 地址。
1. **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 是一个**多层协议构成的框架体系**，每一层都以相邻的下层为基础，同时又是相邻上层的基础，直至达到应用层为止。你可以参考下面的图片。

<img src="https://static001.geekbang.org/resource/image/99/a6/99edd1ea26dcd96eb73c6bb4acde4da6.jpg" alt="">

这里我重点介绍一下第三层（从下往上数），它基于 HTTP、HTTPU、HTTPMU协议，属于传送协议层。传送的内容都是经过“封装”之后，存放在特定的XML文件中的。用于**设备和服务发现**的SSDP（Simple Service Discovery Protocol，简单服务发现协议）协议就是基于XML 传送数据的。

你不要被SSDP协议的名字迷惑了，其实它既提供了服务发现的功能，也提供了设备发现的功能。我们可以基于 SSDP中的M-SEARCH方法来查询设备，然后基于设备的响应，获得设备的服务能力的描述信息。同时设备可以通过NOTIFY 方法向网络通知自己的服务能力。

借助UPnP协议，你的设备就可以自动被发现和使用了。这个自动化的过程，我们通常用一个专有名词来概括，也就是零配置组网。梳理一下，零配置组网包括三个方面的技术内核：

1. 为网络设备自动分配 IP 地址，一般涉及DHCP协议和AutoIP方法；
1. 自动发现和解析设备，主要是基于SSDP协议；
1. 自动传播和发现各网络设备提供的服务，主要也是基于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”范围的地址。
1. 第二步是自动发现和解析设备的名称。在UPnP中一般是基于SSDP协议，另一种替代的方案是 mDNS 协议。
1. 第三步是自动传播和发现各网络设备提供的服务，一种选择是采用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/)详细了解。

我这里总结了一个思维导图，供你参考。

<img src="https://static001.geekbang.org/resource/image/6e/7b/6e34893e226d5de8e5a5aa6d5059437b.jpg" alt="">

### 思考题

最后，我还是给你留一个思考题吧。

在 DHCP 协议的流程介绍中，我提到当设备收到 DHCP 服务器响应的 DHCP ACK 消息后，并不会立即使用这个 IP 地址，而是要先检测一下 IP 地址是否有效。那设备是通过什么方式检测 IP 地址的有效性的呢？

你可以在留言区和我交流你的思考。同时，也欢迎你将这一讲分享给你的朋友一起交流学习。
