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 秒实验
- 用普通用户权限跑同一段代码,看 SOCK_RAW 那两行的报错文本。
- 试着建
AF_UNIX的套接字,看它返回什么。 - 想一下:为什么「标准要求」比「实际实现」容易写?
常见误区
- 以为 OSI 输在「设计不好」。 它输在「太完整、做不出来」,而 TCP/IP 已经有能跑的实现了。
- 以为「标准 compliant」就等于「行为一致」。 各家实现有细微差异,极端条件下表现不同。
- 以为「文档说支持」就等于「你这儿能用」。 要看权限、平台、版本, 这两种情况处理方式完全不同。
自测三问(点开对答案)
1. TCP/IP 与 OSI 的标准化顺序有什么不同? —— **OSI 先定标准再写实现,TCP/IP 先有能跑的实现再写文档。**后者不会有「实现不了」的条款, 所以赢了。 2. 为什么要求「多个独立实现」? —— **只有一份实现时,标准的歧义永远发现不了。**两家一对接,歧义立刻暴露——这是质量保证机制。 3. 「权限不足」和「协议不支持」为什么要分清? —— **前者是标准支持但不给你用,可以换权限解决;后者是实现没做,只能绕过。** 排查方向完全不同。下一节讲标准的具体载体:RFC 文档。
- ☐ (本节完,下一节 2.2.3 TCP/IP规范——RFC)