📚 第二章 TCP/IP基础知识
📄 一句话概括

互联网不是「被设计出来的网络」,是「都用同一套协议的网络」。 IP 是窄腰:上面无限种应用,下面无限种介质,中间只有它一个。

📝 本节和前面几节的关系

2.3.1 讲的是「互联网这个词」,这一节讲「它凭什么能连起来」。 这一节用一次域名解析就能验证「换掉网络层不动应用层」。

一、关系是「都用它」,不是「属于它」

两句话,方向完全不同:

说法 对错 为什么
「互联网属于 TCP/IP」 反了 TCP/IP 是一套协议
「TCP/IP 属于互联网」 也反了 协议是公开的标准
「互联网 = 跑 TCP/IP 的网络」 对 是「都用」的关系

前两行都是「归属关系」,都不成立:TCP/IP 是 IETF 维护的公开标准,不属于任何组织; 互联网也不是「一个东西」,没有主体。 所以准确的说法是第三行:当足够多的网络都用同一套协议互联时,它们合起来就叫互联网。 这个理解很重要,因为它解释了「为什么没人能关掉互联网」: 要关掉它,得让所有网络同时停用 IP——而没有任何一个机构有这个权力。

二、窄腰结构

一张图能说清:

应用层   HTTP  DNS  SMTP  SSH  ……(无限多)
            \   |    |     |   /
传输层        TCP      UDP
                 \      /
网络层            IP          ← 窄腰
                 /      \
链路层      以太网    Wi-Fi  光纤  4G  ……(无限多)

中间那个 IP 是唯一的「必经之路」:上面可以有无数种应用协议,下面可以有无数种传输介质, 但它们都必须穿过 IP 这一层。 这就是「窄腰」的好处:只需要把 IP 实现好,上下两头就能自由生长。 新加一种介质(比如 5G),只要它能承载 IP,上面所有应用立刻就能用,不需要任何应用改代码。 代价也很清楚:IP 本身很难改。 IPv4 地址不够用这件事,从 1990 年代就知道,但直到今天还在用 NAT 撑着—— 因为改 IP 就是改整个互联网的地基。

三、一次解析证明一切

「换掉网络层,应用层不动」这句话可以实测:

| 地址族 | 返回条数 | 应用代码要改吗 | |---|---| | IPv6 | 4 | 不用 | | IPv4 | 4 | 不用 |

同一个 getaddrinfo 调用,同时返回两代地址:而调用它的应用代码一行都不用改。 这就是「窄腰」在实践中的样子——你写的程序既能在 IPv4 上跑,也能在 IPv6 上跑, 因为「选哪个地址」这件事交给了系统。 这里有个容易被忽略的细节: getaddrinfo 一个函数干三件事——查 DNS、挑协议、填好可以直接 connect 的地址结构。 所以现代程序很少自己拼 sockaddr,这件事交给系统,才能同时兼容两代协议。 反过来说,如果程序自己拼了 IPv4 的结构,换到 IPv6 就得改代码——这就是「绕过窄腰」的代价。

四、动手 Demo:同一个域名,两套地址

查同一个域名,看它同时给出几代地址。 关键是「应用代码一行没改」。

import socket

infos = socket.getaddrinfo('www.python.org', 443,
                           proto=socket.IPPROTO_TCP)
seen = set()
for fam, _t, _p, _c, sa in infos:
    if (fam, sa[0]) in seen:
        continue
    seen.add((fam, sa[0]))
    print('%-6s %s' % ('IPv6' if fam == socket.AF_INET6 else 'IPv4',
                       sa[0]))

实跑输出:

--------------------------------------------------------------------
同一个域名,两套地址
--------------------------------------------------------------------
**「互联网 = TCP/IP 的网络」这句话,用一次解析就能验证。**下面查同一个域名,
看它同时给出几代地址。

  地址族     地址
  ------------------------------
  IPv6       2a04:4e42::223
  IPv6       2a04:4e42:200::223
  IPv6       2a04:4e42:400::223
  IPv6       2a04:4e42:600::223
  IPv4       151.101.0.223
  IPv4       151.101.192.223
  IPv4       151.101.128.223
  IPv4       151.101.64.223

**同一个 `getaddrinfo` 调用,同时返回 IPv4 与 IPv6 地址**:**而调用它的应用代码一行都不用改。**
**这就是「换掉网络层,传输层与应用层不动」的实证。**

**注意这里 `getaddrinfo` 一个函数就干了三件事**:
**查 DNS、挑协议、填好可以直接 `connect` 的地址结构。**
**所以现代程序很少自己拼 `sockaddr`**——**这件事交给系统,才能同时兼容两代协议。**

IPv6 四条、IPv4 四条:同一个域名,两代地址各给了四个, 这是 CDN 的常见做法(多个节点、多个地址)。 注意顺序是先 IPv6 后 IPv4:getaddrinfo 默认把 IPv6 排在前面, 系统会优先尝试 IPv6,失败了再退回 IPv4。 这个「先六后四」的行为叫 Happy Eyeballs,目的是避免「IPv6 不通时卡住很久」。 这段代码一行 IPv4/IPv6 的判断都没有:它只是把系统给的地址列出来。 真正写客户端时,你连这个循环都不用写——直接把 getaddrinfo 的结果喂给 connect 就行。

30 秒实验

  1. 把域名换成 www.qq.com,看返回几代地址、各几条。
  2. 用 socket.AF_INET 强制只查 IPv4,看结果差多少。
  3. 想一下:如果一个程序写死了 AF_INET,换到纯 IPv6 网络会怎样?

常见误区

  1. 以为「互联网属于某个组织」。 它没有主体,是一堆网络自愿互联的结果。
  2. 以为 TCP/IP 是「互联网公司的协议」。 它是 IETF 维护的公开标准,谁都能实现。
  3. 以为「窄腰」只是比喻。 它是结构约束:新介质只要能承载 IP,上面所有应用立刻可用。
自测三问(点开对答案) 1. 互联网与 TCP/IP 是什么关系? —— **「都用」的关系,不是「属于」**:当足够多的网络都用同一套协议互联时,它们合起来就叫互联网。 2. 「窄腰」指什么?好处和代价各是什么? —— **指 IP 是唯一的必经之路**。好处是上下两头能自由生长; 代价是 IP 本身极难改(IPv4 地址耗尽只能靠 NAT 撑着)。 3. 为什么应用代码换 IPv4/IPv6 不用改? —— **因为选地址这件事交给了 `getaddrinfo`**:它一次返回两代地址,程序照着连就行。

下一节讲互联网的整体结构:谁连谁。

相关笔记