📚 第四章 IP协议
📄 一句话概括

128 位写成八组四位十六进制,用冒号分隔。 两条省略规则:前导零可省、连续零组用「::」,但「::」只能出现一次。

📝 本节和前面几节的关系

4.3.1 讲过 IPv4 的写法,这里换成 IPv6 的。 4.6.4 会讲地址内部怎么划分,先学会「怎么把一个地址写对」。

一、基本写法:八组十六进制

128 位除以 16 等于 8,所以是八组。 每组四位十六进制,范围 0000 到 ffff。

项 IPv4 IPv6
进制 十进制 十六进制
分隔符 点 冒号
组数 4 组 8 组
一组几位 8 位 16 位

第三行「8 组」是硬规定: 展开写法必须凑满八组,缺的补零——这一点在「把压缩写法还原」时特别重要。

二、两条省略规则

规则一:每组前面的零可以省。 0001 写成 1,00ab 写成 ab,0000 写成 0。 规则二:连续的全零组可以用「::」一次代替。 注意是「连续」且「只能一次」。

完整写法 压缩后 省在哪儿
2001:0db8:0000:0000:0000:ff00:0042:8329 2001:db8::ff00:42:8329 前导零 + 三组零
fe80:0000:0000:0000:0202:b3ff:fe1e:8329 fe80::202:b3ff:fe1e:8329 同上
0000:0000:0000:0000:0000:0000:0000:0001 ::1 七组零

三行都同时用了两条规则:先省前导零,再把连续零换成 ::。 顺序不能反——先换 :: 的话,剩下的组就没法数了。

三、为什么「::」只能一次

因为「::」代表「若干组零」,而组数必须是 8。 如果出现两个「::」,就没法算出每个代表几组。

写法 能不能还原 原因
2001:db8::1:0:0:1 能 一个 :: 代表 2 组
2001:db8::1::1 不能 两个 :: 算不清

第一行那个例子很典型: 原地址是 2001:0db8:0000:0000:0001:0000:0000:0001,有两段连续零,但只能压前一段, 后一段老老实实写成 0:0。 所以压缩结果看着有点怪,但它是对的。 还原的办法是「数剩下的组」:八组减去已经写出来的组数,剩下的就是 :: 代表的组数。 这也是为什么「::」不能一次用两个。

四、前缀长度怎么写

和 IPv4 一样用斜杠:2001:db8::/32。 但含义要清楚:/32 指的是「前 32 位是网络部分」。

写法 含义 谁用
2000::/3 全球单播的总范围 协议规定
2001:db8::/32 文档示例专用 写书、写文档
fd00::/8 唯一本地地址 内网
fe80::/10 链路本地地址 同一链路

第二行那个 2001:db8::/32 要记牢:它是 IANA 专门留给「文档和例子」的, 不会和真实网络冲突。 所以看到教程里用这个地址,说明作者是守规矩的。

五、动手 Demo:IPv6 地址怎么写才不啰嗦

用 ipaddress 把几种写法压缩、再展开,看省略规则到底怎么生效。 注意第三行那个「压一半」的结果:2001:db8::1:0:0:1。

import ipaddress
for s in ('2001:0db8:0000:0000:0001:0000:0000:0001',
          'fe80:0000:0000:0000:0202:b3ff:fe1e:8329',
          '0000:0000:0000:0000:0000:0000:0000:0001'):
    a = ipaddress.IPv6Address(s)
    print(a.compressed, a.exploded.count(':') + 1)

实跑输出:

--------------------------------------------------------------------
IPv6 地址怎么写才不啰嗦
--------------------------------------------------------------------
**128 位写成 8 组十六进制太长了**,
**所以有两条省略规则:前导零可以省、连续的全零组可以用「::」代替。**
**注意「::」只能出现一次**——否则就没法还原成 8 组了。

  完整写法                                    压缩后
  ---------------------------------------------------------------------
  2001:0db8:0000:0000:0000:ff00:0042:8329     2001:db8::ff00:42:8329
  fe80:0000:0000:0000:0202:b3ff:fe1e:8329     fe80::202:b3ff:fe1e:8329
  2001:0db8:0000:0000:0001:0000:0000:0001     2001:db8::1:0:0:1
  0000:0000:0000:0000:0000:0000:0000:0001     ::1
  0000:0000:0000:0000:0000:0000:0000:0000     ::

**第三行是关键**:**有「两段全零」,只压了前一段(变成 ::),后一段保留成 0:0。**
**因为再压一次就没法判断「::」代表几组零了。**

**反过来,压缩写法也能展开回 8 组**:
  压缩写法       展开                                        组数
  ----------------------------------------------------------------
  ::1            0000:0000:0000:0000:0000:0000:0000:0001     8
  fe80::1        fe80:0000:0000:0000:0000:0000:0000:0001     8
  2001:db8::     2001:0db8:0000:0000:0000:0000:0000:0000     8

**最后一列永远是 8**——**这就是「只能压一处」的原因:组数是固定的。**

**还有一条容易踩的**:**「::」和「::1」不是一回事,前者是全零地址(未指定),后者是回环。**
**写配置时少一个 1,行为完全不同。**

compressed 和 exploded 是一对:一个给「最短写法」,一个给「完整的八组」。 写配置用前者,做解析和比较用后者。 第三行那个结果为什么只压了前一段:ipaddress 的实现是「找最长的一段连续零」, 两段一样长时取靠前的。所以它压前面那段,后面保留 0:0。 这不是 bug,是 RFC 允许的自由选择。 第二张表的「组数永远是 8」是最实用的自检手段: 拿到一个压缩地址,展开数一下是不是 8 组,就知道它有没有写错。 命令行里 ping6 报「地址不合法」时,先数这个。

30 秒实验

  1. 写出 2001:db8:0:0:1:0:0:1 压缩后的样子。
  2. 想一下:为什么「::」不能出现两次?
  3. 算一下:fe80::1 展开后有几个 0?

常见误区

  1. 以为「::」可以随便用几次。 只能一次,否则无法确定每组零代表几位。
  2. 以为「前导零不能省」。 可以省,0001 就是 1。
  3. 以为「2001:db8:: 是真实地址」。 是文档专用段,专门给例子用。
自测三问(点开对答案) 1. IPv6 地址由几组十六进制组成? —— **八组**,每组 16 位,加起来 128 位。 2. 「::」的两条限制是什么? —— **只能出现一次,而且只能代表连续的全零组。** 3. 2001:db8::/32 是干什么用的? —— **文档和示例专用**,不会与真实网络冲突。

下一节拆开看:这 128 位内部是怎么划分的。

相关笔记