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

TCP/IP 的标准化是「先有实现、后有文档」,OSI 是反过来——结果前者赢了。 精髓只有一句:能被广泛实现的,才配叫标准。

📝 本节和前面几节的关系

2.2.1 讲清了「是什么」,这一节讲「凭什么它成了标准」。 这一节会与 OSI 反复对照,两套思路的差别值得记住。

一、两条路

标准化有两条完全不同的路径:

路径 顺序 代表 结果
先标准后实现 定标准 → 做产品 OSI 完整但没赢
先实现后标准 跑起来 → 写文档 TCP/IP 赢了

第一行不是「设计得不好」,而是「太完整了」:OSI 七层每一层都定义得很细, 细到厂商在标准落地之前根本做不出产品。 等产品做出来,TCP/IP 已经装满了全世界的机器。 第二行那个「跑起来」是关键词: TCP/IP 的第一份文档(RFC 791/793)是在「已经有能跑的实现」之后写的, 所以它写的全是「我们实际怎么做的」,不是「我们希望大家怎么做」。 这个顺序决定了它不会有「实现不了」的条款。

二、精髓落在哪

三条,每条都能在 RFC 里找到痕迹:

原则 具体做法 好处
粗共识 能跑就行,不追求完美 推进快
多个独立实现 至少两份能对接 歧义会被逼出来
向后兼容 旧的不废弃,只加新选项 升级不用全网切

第二行是质量保证机制: 只有一份实现时,标准里的歧义永远发现不了——因为它自己跟自己总是一致的。 两家独立实现一对接,歧义立刻暴露。 所以 IETF 要求「Proposed Standard 必须有多个独立实现」。 第三行解释了为什么老协议能活几十年:HTTP/1.0 的客户端今天还能用,SMTP 的语法三十年没大改。 因为标准是「累加」的,不是「替换」的。 代价是文档越来越厚、实现越来越复杂——这是「兼容」要付的长期成本。

三、标准与实现的距离

「应当支持」和「实际可用」是两件事:

| 说法 | 含义 | 例子 | |---|---| | 标准要求 | 「实现应当支持 X」 | 文档里写了 | | 实际可用 | 这台机器上真的能用 | 要看权限、平台 |

这两者的差距在 Demo 里会直接看到: 同一段代码建原始套接字,在权限高的环境里返回「可以」,在普通权限下返回「权限不足」。 两者都不违反标准——因为标准说的是「应当支持这个组合」,不是「必须对所有用户开放」。 理解这一点很实际: 遇到「文档说支持但我这儿不行」时,先分清是「标准没要求」还是「实现没做到」, 这两种情况的处理方式完全不同——前者只能绕过,后者可以报 bug。

四、动手 Demo:不是所有「地址族 × 类型」的组合都能用

把六种常见组合真建一遍,看哪些成功。 结果取决于权限,所以每一行的「可以」都要带着条件读。

import socket

fams = [('AF_INET', socket.AF_INET), ('AF_INET6', socket.AF_INET6)]
kinds = [('SOCK_STREAM', socket.SOCK_STREAM),
         ('SOCK_DGRAM', socket.SOCK_DGRAM),
         ('SOCK_RAW', socket.SOCK_RAW)]
for fn, fv in fams:
    for kn, kv in kinds:
        try:
            s = socket.socket(fv, kv)
            s.close()
            print('%-10s %-12s 可以' % (fn, kn))
        except OSError as e:
            print('%-10s %-12s 不行(%s)' % (fn, kn, e.strerror[:16]))

实跑输出:

--------------------------------------------------------------------
不是所有「地址族 × 类型」的组合都能用
--------------------------------------------------------------------
**标准写「应当支持」,实现可以做不到。**下面把常见的组合真建一遍,看哪些成功。

  地址族       套接字类型      本机能不能建
  ------------------------------------------
  AF_INET      SOCK_STREAM     可以
  AF_INET      SOCK_DGRAM      可以
  AF_INET      SOCK_RAW        可以
  AF_INET6     SOCK_STREAM     可以
  AF_INET6     SOCK_DGRAM      可以
  AF_INET6     SOCK_RAW        可以

**这张表在本机全部是「可以」**——**包括通常要管理员权限的 SOCK_RAW。**
**说明跑这段代码的进程权限不低。**
**换一台普通权限的机器,SOCK_RAW 那两行很可能变成「不行」**,
报的是「权限不足」而不是「协议不支持」。
**这张表也说明「标准化」到底统一了什么**:
**它统一了「应当有哪些组合」,但没规定「必须全部可用」。**
**所以同一段代码在不同系统、不同权限下行为可能不同**——
**这正是「标准与实现之间有距离」的直接证据。**

这张表在本机全部是「可以」,包括通常要管理员权限的 SOCK_RAW: 说明跑这段代码的进程权限不低。 换一台普通权限的机器,SOCK_RAW 那两行很可能变成「权限不足」。 注意报错文本的区别: 「权限不足」说明协议支持、只是不给你用;「协议不支持」说明这个系统压根没实现。 这两种错要分清,否则会往完全错的方向排查。 所以「标准化」统一的是「应当有哪些组合」,不是「必须全部可用」: 同一段代码在不同系统、不同权限下行为可能不同——这正是「标准与实现之间有距离」的直接证据。

30 秒实验

  1. 用普通用户权限跑同一段代码,看 SOCK_RAW 那两行的报错文本。
  2. 试着建 AF_UNIX 的套接字,看它返回什么。
  3. 想一下:为什么「标准要求」比「实际实现」容易写?

常见误区

  1. 以为 OSI 输在「设计不好」。 它输在「太完整、做不出来」,而 TCP/IP 已经有能跑的实现了。
  2. 以为「标准 compliant」就等于「行为一致」。 各家实现有细微差异,极端条件下表现不同。
  3. 以为「文档说支持」就等于「你这儿能用」。 要看权限、平台、版本, 这两种情况处理方式完全不同。
自测三问(点开对答案) 1. TCP/IP 与 OSI 的标准化顺序有什么不同? —— **OSI 先定标准再写实现,TCP/IP 先有能跑的实现再写文档。**后者不会有「实现不了」的条款, 所以赢了。 2. 为什么要求「多个独立实现」? —— **只有一份实现时,标准的歧义永远发现不了。**两家一对接,歧义立刻暴露——这是质量保证机制。 3. 「权限不足」和「协议不支持」为什么要分清? —— **前者是标准支持但不给你用,可以换权限解决;后者是实现没做,只能绕过。** 排查方向完全不同。

下一节讲标准的具体载体:RFC 文档。

相关笔记