📚 第一章 网络基础知识
📄 一句话概括

协议不是网络专有名词,它是「约好的做法」。 网络协议只是把「约好的做法」写成了字节层面的规则。

📝 本节和前面几节的关系

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 秒实验

  1. 试试 getprotobyname('ipv6'),看本机有没有。
  2. 反查:用 socket.getservbyname('http') 查 HTTP 的端口号, 对比一下「协议号」与「端口号」是两个不同的东西。
  3. 查一下 IP 头里协议号占几个比特,算一下最多能有多少种上层协议。

常见误区

  1. 以为协议就是「标准文档」。 文档只是记录,协议是「双方实际遵守的做法」。 很多协议是先有实现、后有文档的(TCP/IP 就是这样)。
  2. 以为协议号与端口号是一回事。 协议号在 IP 头里(8 位), 端口号在 TCP/UDP 头里(16 位),层次不同、作用不同。
  3. 以为协议越精确越好。 精确是以「实现成本」换来的, 所以协议设计里到处是「允许但不要求」的条款。
自测三问(点开对答案) 1. 为什么说协议的本质是「共同约定」而不是「单方面规范」? —— **因为单方面守约没有意义**:一方按规则发、另一方不按规则收,通信照样失败。 **所以协议的变更必须两边一起改,这就是老协议活得久的原因。** 2. IP 头里给协议号的字段有几个比特?这限制了什麼? —— **8 位,最多 256 种上层协议。** **这个位数很小是故意的**——它逼着上层协议尽量少而通用。 3. 为什么协议里要写版本号? —— **让新旧实现能共存**。没有版本号,升级就得全网同时切换,**这在互联网上基本不可能实现。**

下一节讲「为什么非得有协议」——用一个反例说明:不按协议来,对端会怎么处理。

相关笔记