协议不是网络专有名词,它是「约好的做法」。 网络协议只是把「约好的做法」写成了字节层面的规则。
1.1 和 1.2 讲完了网络的来龙去脉,1.3 开始讲原理。 这一节先把「协议」这个词从抽象拉到日常,后面几节再往字节层面走。
一、生活里的协议
打电话、寄快递、握手,都是协议——因为它们都有「谁先说、说什么、对方怎么回」的约定。
| 场景 | 约定 | 不遵守会怎样 |
|---|---|---|
| 打电话 | 先响铃,等对方接 | 直接说话,对方没听见 |
| 寄快递 | 填单、贴单、交件 | 件到了没人认领 |
| 握手 | 伸手、相握、松开 | 尴尬,但不会出错 |
这些约定的共同点是「双方都要遵守」: 单方面守约没有意义——协议的本质是「共同约定」,不是「单方面的规范」。 所以协议一旦定下来,改起来就慢:要改就得两边一起改,这就解释了为什么老的协议能活几十年。
二、计算机协议的不同点
人的协议可以含糊,计算机的不行:
| 维度 | 人的协议 | 计算机协议 |
|---|---|---|
| 精度 | 可以含糊 | 每个比特都有规定 |
| 容错 | 能猜 | 错了就报错 |
| 变更 | 慢慢磨合 | 要留兼容版本号 |
第二行那个「错了就报错」是计算机协议的硬性要求:因为机器没有「猜」的能力, 所以协议里必须写清楚「不合法怎么办」——这就是各种错误码的由来。 这也是为什么读协议文档时,「正常流程」与「异常流程」的篇幅往往一样多。
三、一个最小的例子
HTTP 请求的第一行就是一条极简协议:
GET /index.html HTTP/1.1
这三段各有各的约定:
| 段 | 约定 | 不写会怎样 |
|---|---|---|
GET |
方法,必须是注册过的 | 服务端回 501 |
/index.html |
路径,以 / 开头 | 可能回 400 |
HTTP/1.1 |
版本,决定后续规则 | 按 0.9 处理或报错 |
第三行那个「版本决定后续规则」是协议设计的关键: 版本号让新旧实现能共存——服务端看到 1.1 就按 1.1 的规则答,看到 1.0 就按 1.0 答。 没有版本号的协议,升级必然是一次「全网同时切换」,这在互联网上几乎不可能。
四、动手 Demo:协议号是一张人人都认的表
问本机操作系统要几个常见协议的编号。 这些编号不是名字,是写进 IP 头的 8 位数字——下层只认数字,不认名字。
import socket
for name in ('icmp', 'tcp', 'udp', 'gre', 'esp', 'ah', 'sctp'):
try:
print('%-6s %d' % (name, socket.getprotobyname(name)))
except OSError:
print('%-6s 本机没登记' % name)
实跑输出:
--------------------------------------------------------------------
协议号是一张人人都认的表
--------------------------------------------------------------------
**协议号不是随便起的名字,是一串数字,写进 IP 头里。**
**下面这张表是问本机操作系统要来的。**
协议名 IP 头里的协议号
---------------------------
icmp 1
tcp 6
udp 17
gre 本机没登记
esp 50
ah 51
sctp 本机没登记
**TCP 是 6、UDP 是 17、ICMP 是 1**——这三个数字在每一个 IP 包里都占 1 个字节。
**为什么要有这张表**:**IP 头里只有 8 位给协议号,最多 256 种上层协议。**
**这个 8 位字段就是「分层」最硬的证据**——上层是谁,下层根本不知道,只认这 8 位数字。
TCP 是 6、UDP 是 17、ICMP 是 1,这三个要记住。
gre 与 sctp 显示「本机没登记」不是错误——getprotobyname 查的是本机的协议表,
表里没有不代表协议号不存在,只是本机没装。
这个 8 位字段就是「分层」最硬的证据:
IP 层只知道「上面是 6 号」,完全不知道 6 号是 TCP、更不知道里面装的是 HTTP。
知道得越少,越容易换。
30 秒实验
- 试试
getprotobyname('ipv6'),看本机有没有。 - 反查:用
socket.getservbyname('http')查 HTTP 的端口号, 对比一下「协议号」与「端口号」是两个不同的东西。 - 查一下 IP 头里协议号占几个比特,算一下最多能有多少种上层协议。
常见误区
- 以为协议就是「标准文档」。 文档只是记录,协议是「双方实际遵守的做法」。 很多协议是先有实现、后有文档的(TCP/IP 就是这样)。
- 以为协议号与端口号是一回事。 协议号在 IP 头里(8 位), 端口号在 TCP/UDP 头里(16 位),层次不同、作用不同。
- 以为协议越精确越好。 精确是以「实现成本」换来的, 所以协议设计里到处是「允许但不要求」的条款。
自测三问(点开对答案)
1. 为什么说协议的本质是「共同约定」而不是「单方面规范」? —— **因为单方面守约没有意义**:一方按规则发、另一方不按规则收,通信照样失败。 **所以协议的变更必须两边一起改,这就是老协议活得久的原因。** 2. IP 头里给协议号的字段有几个比特?这限制了什麼? —— **8 位,最多 256 种上层协议。** **这个位数很小是故意的**——它逼着上层协议尽量少而通用。 3. 为什么协议里要写版本号? —— **让新旧实现能共存**。没有版本号,升级就得全网同时切换,**这在互联网上基本不可能实现。**下一节讲「为什么非得有协议」——用一个反例说明:不按协议来,对端会怎么处理。
- ☐ (本节完,下一节 1.3.2 协议的必要性)