9.5. IP 地址¶
硬件地址能在同一条本地线缆或无线小区中把一台设备从其他设备里区分出来。它胜任这项工作,但在本地网段之外就毫无用处了:隔壁楼里的交换机无法依据它来路由,因为让交换机学习 MAC 地址的底层机制只在一个网段内有效。
网络层用第二种地址来解决这个问题,这种地址与设备插在哪条线缆上无关。这种地址的名称是互联网协议地址(Internet Protocol address),即 IP 地址,而其中“互联网协议”这一半,命名的是互联网上每台主机在发送或转发数据时所遵循的一套规则。当前的互联网并行使用着两个版本的寻址方案——IPv4(较旧的形式,在小型网络上仍占主导地位)和 IPv6(较新的形式,正在逐渐取代前者)。
9.5.1. 什么是 IP 地址¶
IP 地址是一个足够大的数字,足以唯一地标识互联网上的任何设备。它以一种供人阅读的形式书写(本节其余部分都使用这种形式),但在数据包头部中,它只是一个固定大小的整数。
IPv4 地址长度为 32 位,写作四个用点分隔的十进制数,每个数为一个字节:
192.168.1.42 8.8.8.8 10.0.0.1
32 位可提供约四十亿个可能的地址,这在 IPv4 于 20 世纪 70 年代设计时听起来绰绰有余,但到 2010 年代初却已不够用了。
IPv6 地址长度为 128 位,写作八组用冒号分隔的四位十六进制数:
2001:0db8:85a3:0000:0000:8a2e:0370:7334连续的零可以缩写为
::,每组中的前导零也可以省略,因此上面的地址通常写作2001:db8:85a3::8a2e:370:7334。
除此之外,这两个地址族就像两种不同的语言;如果没有网关的帮助,IPv4 主机无法直接向 IPv6 主机发送数据包。摄像头的 network 和 socket 模块都支持二者。本教程的示例使用 IPv4,因为摄像头将要加入的大多数本地网络仍然是仅 IPv4 的,但只要把地址替换掉,后续的所有内容对 IPv6 都完全适用。
9.5.2. IP 地址的用途¶
IP 地址说明了某个数据包是发给互联网上哪台主机的。一个不知道如何直接到达目的地的路由器,知道另外某个路由器很可能知道,于是把数据包转发过去。数据包在路由器之间逐跳传递,每一跳都离目的地更近一点,直到某个确实位于目的地本地网段上的路由器完成最后一跳的投递。
正是这种逐跳行为,让互联网得以作为一个大网络运作,而不是许多互不相连的小岛。下一页介绍这些跳跃是如何选择的;本页只讲地址。
9.5.3. 摄像头如何获得 IP 地址¶
刚加入某个 Wi-Fi 网络的摄像头,在能与任何东西通信之前都需要一个 IP 地址。实现这一点通常有两种常见方式。
第一种是自动分配。摄像头向本地网络请求一个地址;负责发放地址的设备是路由器——也就是把本地网络连接到更广阔互联网的那个盒子。在大多数家庭和小型办公环境中,同一个物理盒子还同时充当有线设备插入的交换机,以及无线设备关联的 Wi-Fi 接入点,因此“路由器”“交换机”和“接入点”可能都是同一件硬件。路由器运行着一个名为 DHCP(动态主机配置协议,Dynamic Host Configuration Protocol)的小型服务,它维护一个可用地址池,为每台新接入的设备挑选一个地址,并租用给它一段固定的时间。在 DHCP 工作的同时,路由器还会向摄像头交付另外几项有用的配置:
当目的地不在本地网络上时,发往哪个地址来传出流量(即默认网关,也就是路由器自身的地址);以及
一台或多台名称服务器的地址,它们负责把像
example.com这样供人阅读的名称转换成 IP 地址。这项名称查询服务称为 DNS,即域名系统(Domain Name System),名称与 DNS 对其有详细介绍。
所有这些都在链路建立的过程中自动完成。摄像头无需显式地请求其中任何一项;在上一页的示例中,当 isconnected() 返回 True 的那一刻,摄像头就已经拥有了自己的地址、网关和名称服务器。
第二种选择是静态配置。某些部署希望摄像头有一个已知的地址,这样其他设备无需先查询就能找到它。ipconfig() 方法可以手动设置地址、网关和名称服务器:
wlan.ipconfig(addr4=("192.168.1.50/24", "192.168.1.1"))
wlan.ipconfig(dns="192.168.1.1")
静态配置很脆弱(两台设备如果不小心被分配了相同的地址就会冲突)。除非出现需要覆盖默认设置的特定理由,否则请优先使用 DHCP 默认方式。
摄像头一旦拥有了 IP 地址,它就加入了互联网(或者至少加入了本地网络的那一部分)。其他设备现在可以用这个地址向它发送数据包,它也可以向它们发送数据包。
9.5.4. 子网掩码与 /24¶
上面静态配置示例中地址末尾的 /24 就是子网掩码。IP 地址本身并不说明本地网络在哪里结束——192.168.1.50 可能是一个小型家庭网络上几百个地址中的一个,也可能是一个较大网络上数千个地址中的一个。子网掩码说明了地址中有多少位用于命名网络,有多少位用于命名其中的主机。
/24 的意思是“32 位中前 24 位命名网络;后 8 位命名主机”。对于 192.168.1.50/24,这会把地址拆分为表示网络的 192.168.1 和表示主机的 .50,从而在同一本地网络上为约 254 台设备留出空间。/16 会为主机部分留出更多位,从而在一个网络上容纳多得多的设备;/30 只会留出两个主机地址,正好适合一条点对点链路。
子网掩码通常也写成与地址相同的点分四字节数字形式。/24 等价于 255.255.255.0——把每个字节读作“所有属于网络部分的位”。这两种形式可以互换;下一小节中 ipconfig() 读取出来的恰好是四字节形式。
这种拆分为何如此重要——设备如何利用子网掩码来判断目的地是在本地网络上、还是需要经由网关发送出去,以及为什么大多数家庭网络都落在 /24 上——这些内容在 专用网络与 NAT 中介绍。
9.5.5. 读回地址¶
不带参数调用 ipconfig() 方法会返回当前生效的配置。addr4 视图返回 IP 地址和子网掩码:
>>> wlan.ipconfig("addr4")
('192.168.1.50', '255.255.255.0')