RFC 1 带读:Host Software——互联网协议协作从这里开始
前言
如果只看标题,RFC 1——Host Software——很容易让人失望:它既不是 TCP,也不是 IP,没有我们今天熟悉的端口、三次握手、路由表,更谈不上 HTTP。
但如果把时间拨回 1969 年 4 月 7 日,它的重要性就完全不同了。
当时 ARPANET 甚至还没有正式投入运行。负责网络交换设备的 **BBN(Bolt, Beranek and Newman)**正在定义 IMP(Interface Message Processor,接口报文处理机),而 UCLA、SRI、UCSB、University of Utah 等站点面对一个更现实的问题:
网络设备把两个计算机接起来之后,两个完全不同的操作系统到底应该怎样“说话”?
RFC 1 讨论的正是这个问题。
它不是一份成熟协议标准,而是一份带着大量开放问题的设计草案:怎样建立 Host-to-Host 连接、怎样进行交互式终端访问、怎样传输大文件、怎样做流量约束、底层已经有校验为什么 Host 之间还要再校验,以及在网络延迟很高时,怎样把一部分交互逻辑下放到本地执行。
从今天看,其中很多具体设计早已淘汰;但它提出的问题,却几乎贯穿了此后几十年的网络协议和分布式软件设计。
本文基于 RFC 1 原文 进行带读,重点不是逐句翻译,而是理解它当时究竟在设计什么。
RFC 基本信息
| 项目 | 内容 |
|---|---|
| RFC | RFC 1 |
| 标题 | Host Software |
| 作者 | Steve Crocker |
| 所属组织 | UCLA |
| 原文日期 | 7 April 1969 |
| 工作组 | Network Working Group |
| 当前 RFC Editor 状态 | Unknown |
| 原文 | RFC 1: Host Software |
| RFC Editor 信息页 | RFC 1 Info |
这里有两个阅读前必须明确的历史背景。
第一,RFC 1 出现时还没有 IETF。今天我们看到的 Standards Track、Proposed Standard、BCP、Historic 等完整 RFC 状态体系,是后来逐步形成的。因此 RFC Editor 对 RFC 1 显示的 Unknown,不能理解成“这是一份仍然可以实现但状态不明的标准”。
第二,RFC 1 本身反复强调其中的内容只是 tentative agreements(暂定共识),还有大量 open questions(开放问题)。它本质上更像今天工作组里公开流转的协议设计讨论稿,而不是最终规范。
历史上它也很快失去了现行协议意义。1971 年的 RFC 100 在对早期 NWG/RFC 进行分类时,已经明确把 RFC 1 标记为 Obsolete。后续 Host-to-Host 协议继续演化到 RFC 33 等文档,最终形成 ARPANET 的 NCP/Host-Host 协议体系。
因此,阅读 RFC 1 的正确姿势不是“照着实现”,而是:
把它当成互联网协议设计刚开始形成时的一张现场照片。
为什么会有这篇 RFC
RFC 1 的第一段几乎已经把问题说完了:
ARPANET 的软件一部分运行在 IMP 中,一部分运行在各个 HOST 中。BBN 负责定义 IMP 的软件,而各个 Host 所在站点必须共同决定 Host 侧的软件应该怎样工作。
这实际上形成了一个非常经典的分层问题:
1 | |
网络连上了,不代表系统能互操作
ARPANET 的目标不是连接几台相同型号的机器,而是连接不同研究机构里的大型计算机和不同操作系统。
所以真正困难的事情不是:
1 | |
而是:
1 | |
这些都不是 IMP 单独能够解决的问题。
IMP 更接近今天网络中的分组交换基础设施:它负责传输、分包、校验和网络内部控制,但并不知道用户是在登录远程系统、传文件,还是操作图形终端。
因此,RFC 1 开始把问题上移到 Host-to-Host Software(主机到主机软件)。
这也是“协议”真正出现的位置
如果两个 Host 都可以自由定义自己的行为,那么即使底层网络完全互通,上层仍然无法通信。
于是不同站点必须共同定义:
- 连接如何建立;
- 连接如何终止;
- 控制消息放在哪里;
- 应用怎样调用网络功能;
- 不同类型的数据怎样传;
- Host 之间怎样验证数据;
- 复杂终端怎样跨网络工作。
这就是协议设计。
RFC 1 的价值之一,正是在非常早期就明确了一个今天看来理所当然的事实:
网络设备解决数据传输问题,端系统仍然需要自己的通信协议。
核心内容
RFC 1 大体可以理解成四层内容:
- 先描述 IMP 已经提供了什么;
- 再提出 Host-to-Host 软件应该满足什么需求;
- 给出一套初步 Host Software 设计;
- 最后设计最早的网络实验验证这些想法。
1. IMP 提供的 Message
RFC 1 中,Host 发送给 Host 的数据单位叫 Message(消息)。
一个 Message:
- 数据长度最多 8080 bits;
- 带有一个 16-bit Header。
Header 被划分成:
| 字段 | 位数 | 含义 |
|---|---|---|
| Destination | 5 bits | 目标 Host 的数字编号 |
| Link | 8 bits | 逻辑 Link 编号 |
| Trace | 1 bit | 请求 IMP 记录消息状态 |
| Spare | 2 bits | 保留、未使用 |
结构可以表示为:
1 | |
这里已经能看到几个有意思的设计。
Destination 只有 5 bits
理论上只能表示:
1 | |
个 Host 编号。
这非常直观地反映了当时网络的规模假设:这不是为几十亿设备设计的地址体系,而是给一个只有少量大型计算机节点的实验网络使用。
Trace Bit
Trace 位被设置后,IMP 会记录该 Message 的状态信息,并把结果发送给 UCLA 的 NMC(Network Measurement Center,网络测量中心)。
从今天的视角看,它很像一种非常原始的网络可观测性机制:
1 | |
它当然不是今天意义上的 OpenTelemetry 或 Distributed Tracing,但“协议中预留观测能力”这个思路并不陌生。
2. Link:IMP 暴露出来的逻辑通道
RFC 1 规定,在每一对 Host 之间存在 32 个 logical full-duplex connections(逻辑全双工连接),称为 Link。
但是这里有一个很重要的细节:
IMP 中的 Link 本身没有“会话开始”和“会话结束”的概念。
原文明确说明,这些 Link always functioning,始终存在。
也就是说:
1 | |
“Connection(连接)”这个状态,是 Host Software 在 Link 之上构造出来的。
这是理解 RFC 1 的一个关键点。
3. RFNM:每条 Link 上的发送节流
IMP 对 Link 增加了一个限制:
一个 Host 在同一条 Link 上发送完一个 Message 后,必须等到目标端 IMP 返回 RFNM(Request for Next Message),才能继续在这条 Link 上发送下一条 Message。
简化后是:
sequenceDiagram
participant A as Source Host
participant IA as Source IMP
participant IB as Destination IMP
participant B as Destination Host
A->>IA: Message #1
IA->>IB: Packet(s)
IB->>B: Reassembled Message #1
IB-->>IA: RFNM
IA-->>A: 可继续发送
A->>IA: Message #2
它起到了某种 flow control / congestion limiting(流量控制 / 拥塞限制) 的作用。
但一定要注意:
RFNM 不是“目标应用已经处理成功”的确认。
它来自 IMP 层,表达的是网络基础设施已经允许发送方继续,而不是远端用户进程已经消费了数据。
不能把 RFNM 直接类比为今天 TCP 的应用语义确认。
4. Message 在 IMP 网络内部会被进一步拆成 Packet
RFC 1 还区分了 Message 与 Packet。
一个 Message 最大 8080 bits。
源 IMP 收到 Message 后,会把它拆成一个或多个 Packet:
- 每个 Packet 最大 1010 bits;
- 每个 Packet 由发送硬件计算 24-bit cyclic checksum;
- 接收端重新计算并比较;
- 到达目标 IMP 后重新组装成 Message。
因此数据流大致是:
flowchart LR
A[Source Host<br/>Message ≤ 8080 bits]
B[Source IMP]
P1[Packet 1<br/>≤ 1010 bits]
P2[Packet 2<br/>≤ 1010 bits]
PN[Packet N<br/>≤ 1010 bits]
C[Destination IMP]
D[Destination Host<br/>Reassembled Message]
A --> B
B --> P1
B --> P2
B --> PN
P1 --> C
P2 --> C
PN --> C
C --> D
这已经是非常清晰的分层:
1 | |
也就是说,Host 不必直接处理网络内部的分包细节。
关键机制与工作流程
RFC 1 真正有意思的地方,在于它不仅描述数据格式,还开始定义 Host Software 的行为。
一、使用 Link 0 建立连接
RFC 1 把 Link 0 保留给两个 Host 的操作系统之间发送控制消息。
其余:
1 | |
作为普通连接使用。
可以把它理解成:
1 | |
注意,这只是理解上的类比,并不是说 RFC 1 已经定义了今天完整意义上的 control plane / data plane。
基本连接流程如下:
sequenceDiagram
participant UA as User Program A
participant OSA as Host A OS
participant OSB as Host B OS
participant UB as User Program B
UA->>OSA: 请求连接 Host B
OSA->>OSA: 选择空闲 Link k
OSA->>OSB: Link 0: REQUEST(Link k)
OSB->>OSA: Link 0: ACCEPT(Link k)
OSA-->>UA: Connection Established
OSB-->>UB: 将该 Link 作为远程 TTY 连接
连接建立后,远端 Host 会把这个连接当成一条刚拨入的 TTY(Teletype,电传打字终端) 线路:
- 产生相同的 echo;
- 等待相同的 login 流程;
- 识别相同的 interrupt character。
换句话说,RFC 1 最先选择的兼容方案并不是设计一种全新的远程应用协议,而是:
先让网络看起来像一根很长的终端线。
这是一个非常务实的 bootstrapping 方案。
二、同时请求同一条 Link 怎么办
假设 Host A 和 Host B 几乎同时选择了同一条空闲 Link。
RFC 1 没有引入复杂的分布式锁,而是提出一个简单的 priority scheme(优先级规则)。
一种可行方案是直接按照 Host ID 排序。
当冲突发生:
- 高优先级 Host 保留自己的选择;
- 低优先级 Host 让步;
- 低优先级 Host 接受对方请求,并重新选择空闲 Link 发起自己的请求。
抽象后就是:
1 | |
这个算法非常简单,但它体现了一个软件设计中一直存在的问题:
分布式系统里,如果两个参与者同时做出互相冲突的决定,必须存在确定性的冲突消解规则。
三、TTY-like Connection 与 File-like Connection
RFC 1 很快意识到:
交互式终端和大文件传输的需求并不一样。
TTY 的处理通常是:
- 一次一个字符;
- 某些字符具有中断语义;
- 缓冲区针对低速交互设计。
如果直接拿这种机制传大文件,会非常低效。
所以 RFC 1 又定义了一种 File-like Connection(文件型连接)。
它与已有 TTY-like Connection 并行存在:
1 | |
File-like Link 的特点:
- 不扫描 interrupt characters;
- 使用更适合高吞吐量的 buffering;
- 建立过程由 Host OS 管理;
- 用户程序不用处理底层 Link 选择细节。
这里已经出现了一个非常重要的设计思想:
不同通信模式不必强行塞进同一种数据通道。
今天可以想到 SSH 的交互流与文件复制、HTTP/2 的多个 stream、QUIC stream、RPC streaming 等很多类似问题。
但这里应该强调:这些只是设计思想上的类比,并不是说现代协议直接继承了 RFC 1 的 TTY/File Link。
四、Host Software 向用户程序提供哪些 Primitive
RFC 1 还明确列出了 Host OS 至少应该提供的 primitive(基础操作):
1 | |
如果换成今天开发者更熟悉的表达,它实际上是在定义一个非常早期的网络 API:
1 | |
RFC 1 并没有形成 BSD Socket 那样成熟的 API,但它已经明确认识到:
网络协议最终必须通过一个稳定的软件接口暴露给用户程序。
五、为什么 IMP 已经有校验,Host 还要再做一次
这是整篇 RFC 最值得今天的软件开发者关注的地方之一。
前面已经看到,IMP-to-IMP Packet 有 24-bit cyclic checksum。
按直觉似乎已经足够。
但 RFC 1 仍然希望增加 Host-to-Host Error Checking。
它提出每个 Message 的 body 中携带:
- message number;
- bit count;
- checksum。
这个 checksum 对 IMP 是透明的,也就是说 IMP 不负责理解。
RFC 1 建议使用:
- 16-bit end-around-carry sum;
- 每 1152 bits 计算后进行一次 1 bit circular right shift;
- 这样希望能够发现 IMP 在 Message 重组时出现的错误。
形成了两层检查:
1 | |
为什么要重复?
因为两层校验保护的边界不同。
IMP 的 checksum 主要保护:
1 | |
而 Host-to-Host checksum 还希望覆盖:
1 | |
这和今天常见的一条工程经验高度一致:
下层保证正确,不等于上层就不需要端到端验证。
但不要把 RFC 1 直接称为后来正式的 End-to-End Argument。那套理论是在更晚的系统研究中被系统化总结的。更准确的说法是:RFC 1 已经非常早地体现出了类似的端到端完整性意识。
六、DEL:把交互逻辑下载到本地执行
RFC 1 最超前的部分可能不是网络传输,而是 DEL。
当时一个远程交互可能需要约 0.5 秒 的响应时间。
如果用户每敲一个键:
1 | |
那么高级图形终端会变得非常难用。
RFC 1 举了一个输入:
1 | |
用户实际想得到的是:
1 | |
其中 <- 表示删除字符。
如果每次键盘输入都往返远端,代价太高。
于是提出:
把处理删除键、回车、本地显示等“trivial responses”所需的前端逻辑放在本地主机执行,只把真正需要远端处理的结果发送过去。
为此设计了一种 DEL(Decode-Encode Language)。
远端 Host 可以把控制终端的 DEL 源程序发送给本地 Host,由本地编译并运行。
工作流程大致是:
sequenceDiagram
participant L as Local Host
participant R as Remote Host / NLS
participant U as Local Terminal
L->>R: 建立连接并登录
L->>R: 请求 Front-End Control
R-->>L: 发送 DEL Program
L->>L: 本地编译 DEL
U->>L: 键盘 / 指针输入
L->>L: 处理 trivial interaction
L->>R: 仅发送需要远端处理的输入
R-->>L: Major Response
L-->>U: 更新终端
它实际上是在做:
1 | |
Steve Crocker 后来在 RFC 2555: 30 Years of RFCs 回顾这段历史时,甚至明确把这种“会话开始时下载一个小程序”的想法与后来出现的 Java / ActiveX 联系起来。
因此,把 DEL 看作一种早期的 downloadable front-end program / mobile code 思想是合理的。
但它不是 JavaScript 的直接祖先,也不是现代浏览器架构的直接技术谱系,更适合作为一种非常早期的相似设计思想来理解。
重点概念与术语
HOST
RFC 1 中大量使用全大写的 HOST。
它指连接到 ARPANET 的主机计算机,不只是今天口语里的“服务器”。
当时每个 Host 往往是一套大型计算机系统,拥有自己的操作系统、终端和用户环境。
IMP
IMP(Interface Message Processor) 是 ARPANET 中负责分组交换的专用计算机。
可以把它粗略理解成现代路由/分组交换基础设施的前身,但它和今天的 IP Router 并不是一回事。
RFC 1 中很重要的一层边界就是:
1 | |
Message
Host 所看到的主要传输单位。
RFC 1 中最大:
1 | |
它会在 IMP 内部被拆分成 Packet。
Packet
IMP-to-IMP 的实际网络传输单位。
最大:
1 | |
并带有:
1 | |
Link
两个 Host 之间由 IMP 提供的逻辑通道。
RFC 1 描述每对 Host 有:
1 | |
但 Link 本身在 IMP 中没有“连接状态”。
Link 0
Host 操作系统之间交换连接控制消息的特殊 Link。
剩余 31 条 Link 用于 TTY-like / file-like 等实际连接。
RFNM
RFNM(Request for Next Message)。
目标端 IMP 发出的控制消息,用来表示同一 Link 上可以继续发送下一条 Message。
它的作用接近网络级发送节流,而不是应用级处理确认。
TTY
TTY(Teletype),电传打字终端。
RFC 1 最基本的远程访问模型就是:
1 | |
File-like Connection
面向大批量数据传输的连接。
它避免 TTY 模式中的字符级特殊处理,并使用更适合高吞吐量的数据缓冲。
DEL
DEL(Decode-Encode Language)。
一种用于描述终端本地控制逻辑的语言,目标是把高频、简单、延迟敏感的交互放在本地执行。
NLS
NLS(oN-Line System),SRI 的交互式在线系统。
RFC 1 计划用 DEL 为 NLS 构建远程前端,并将其作为最初的网络实验之一。
NMC
NMC(Network Measurement Center),位于 UCLA。
当 Message 设置 Trace bit 时,IMP 可以收集状态并将信息返回 NMC。
需要特别注意的点
1. RFC 1 不是今天意义上的正式标准
这是阅读 RFC 1 最容易犯的错误。
原文自己就明确表示内容很多仍未确定,并期待其他站点反馈。
它是一份设计讨论文档,而不是最终 interoperable specification。
2. 当前页面的 Unknown 不等于“仍然有效”
RFC Editor 当前把 RFC 1 状态显示为 Unknown。
但历史文档 RFC 100 已经在 1971 年把 RFC 1 列为 Obsolete。
早期 RFC 的状态关系并不像现代 RFC 一样拥有完整、统一的 Updates / Obsoletes / Obsoleted by 元数据,所以不要机械依赖今天信息页上的状态字段。
3. RFC 1 的 Link 不是 TCP Connection
虽然 RFC 1 使用了 connection、full-duplex 等熟悉词汇,但它的层次与 TCP 不同。
IMP 的 Link:
- 固定存在;
- 每对 Host 总有 32 条;
- IMP 本身不知道会话何时开始或结束。
Host Software 才在 Link 上建立更高层的连接语义。
所以:
1 | |
4. RFNM 也不是 TCP ACK
RFNM 控制同一 Link 上什么时候可以发送下一条 Message。
它不能证明:
- 目标应用已经读取数据;
- 目标应用处理成功;
- 业务操作成功。
把它理解成网络内部的发送许可更加准确。
5. Header 有 8-bit Link 字段,却只有 32 条 Link
这是 RFC 1 自己列出的开放问题之一:
1 | |
作者直接问:
为什么?
这特别能体现 RFC 1 的“现场讨论稿”属性:协议接口甚至还存在明显的不对齐,作者没有替规范强行找理由,而是把问题公开写了出来。
6. 固定 32 Links 的设计扩展性很弱
RFC 1 甚至指出,由于这些 Link 永远存在,IMP 必须持续维护相应表项,不管实际有没有流量。
这种固定预分配结构在小规模实验网络中简单,但随着节点和并发增加,很难扩展。
7. 端到端 checksum 的具体算法已经没有实现价值
RFC 1 提出的:
1 | |
是针对当时 IMP、Host Interface 和 Message reassembly 风险提出的具体机制。
今天阅读它的重点应该是:
1 | |
而不是:
1 | |
更不能把这种非加密 checksum 当成安全完整性机制。
8. DEL 几乎没有今天必需的安全模型
RFC 1 提出的方案允许:
1 | |
这在 1969 年非常大胆。
但如果今天设计同样的机制,就必须马上追问:
- 远端是谁?
- 程序是否经过认证?
- 是否有签名?
- 运行权限是什么?
- 有没有 sandbox?
- 能否访问文件系统?
- 能否访问网络?
- 如何限制 CPU / memory?
- 恶意代码怎么办?
RFC 1 基本没有讨论这些问题。
这是时代局限,而不是一个今天可以直接复制的架构。
9. Host ID 优先级只是非常初级的冲突解决
使用 Host 编号决定连接冲突胜负,好处是:
1 | |
但它依赖:
- 静态 Host 编号;
- 小规模网络;
- 简单连接模型。
现代分布式系统显然需要更复杂的故障、超时、重试、幂等和状态同步机制。
10. 很多今天协议必须具备的内容在 RFC 1 中还没有
例如:
- 完整状态机;
- 明确 timeout;
- retransmission 语义;
- authentication;
- authorization;
- encryption;
- version negotiation;
- capability negotiation;
- 安全威胁模型;
- 大规模地址体系;
- 完整异常恢复流程。
这不是 RFC 1 “写得不好”,而是这些问题很多正是在后来真实部署和协议演化中被逐步发现并系统化的。
从今天的视角重新看这篇 RFC
把具体的 5-bit 地址、32 Links、TTY 和 DEL 暂时拿掉,RFC 1 其实在讨论一些今天仍然非常熟悉的系统设计问题。
1. 网络基础设施与端系统协议之间必须有清晰边界
RFC 1 的基本架构是:
1 | |
今天我们更熟悉:
1 | |
具体层次完全不同,但核心思路一致:
底层网络不应该承担所有上层语义。
2. 网络层可靠,不代表端到端就可靠
IMP 已经对 Packet 做 checksum,Host 仍然要求 Host-to-Host 检查。
这背后是一种非常实用的工程思维:
1 | |
今天无论是:
- 文件传输 hash;
- 对象存储 checksum;
- 数据库 replication 校验;
- RPC payload validation;
- 消息队列业务幂等;
- 端到端数据一致性检查;
本质上都经常面对同一个问题。
3. 延迟敏感逻辑应该尽量靠近用户
DEL 所做的事情,用现代语言可以概括为:
1 | |
高频、简单的操作本地完成,真正需要中心系统处理的请求才过网络。
这和今天很多架构都具有思想上的相似性:
- 浏览器本地交互;
- Edge Computing;
- Client-side validation;
- Remote GUI acceleration;
- 游戏客户端 prediction;
- CDN edge logic;
- IDE Remote Development 的本地/远端职责划分。
不能说这些技术来自 RFC 1,但它们都面对同样的物理规律:
网络 RTT 永远比本地函数调用贵。
4. 控制信息和业务数据最好不要互相污染
RFC 1 用:
1 | |
今天我们会在很多系统里继续看到类似模式:
1 | |
或者:
1 | |
RFC 1 并没有建立今天完整的控制面理论,但把连接管理从普通会话流量中区分出来,是非常自然且重要的设计选择。
5. 协议最终必须映射成程序员可以调用的 API
RFC 1 没有只谈 wire format,还专门列出 Host OS 应该提供给用户程序的 primitives。
这点很容易被忽略。
协议如果只能写在文档上,却无法稳定映射到软件接口,开发者就无法真正使用它。
后来 Socket API 之所以如此重要,也是因为它把复杂的网络行为收敛成了一组稳定、可编程的抽象。
6. 可观测性从网络早期就已经是需求
一个只有 1 bit 的 Trace 标志,看起来不起眼。
但它说明设计者一开始就知道:
网络只“能工作”还不够,还需要知道它是怎么工作的。
Trace → NMC 的机制,本质上服务于:
- 测量;
- 调试;
- 故障定位;
- 网络研究。
对于今天的软件开发者,这个原则甚至更加重要:
1 | |
这篇 RFC 带来的影响
RFC 1 的直接协议设计没有长期保留下来,但它的影响至少可以从三个方向理解。
一、推动 Host-to-Host 协议继续演化
RFC 1 之后,Network Working Group 继续围绕 Host Software 和 Host-Host Protocol 进行大量讨论。
其中很重要的一步是:
- RFC 33 — New Host-Host Protocol
- 以及之后的一系列修订和补充
这些工作逐步形成后来 ARPANET 使用的 Host/Host Protocol / NCP 体系。
2012 年发布的历史文档 RFC 6529: Host/Host Protocol for the ARPA Network 对这段历史做了归档说明:这套 Host/Host Protocol 从 1972 年 1 月开始成为 ARPANET 的正式协议,一直使用到 1983 年 1 月切换到 TCP/IP。
因此可以把演化路线粗略画成:
flowchart LR
A[RFC 1<br/>Host Software<br/>1969]
B[早期 Host-Host 讨论<br/>RFC 2 / 9 / ...]
C[RFC 33<br/>New Host-Host Protocol<br/>1970]
D[ARPANET NCP / Host-Host Protocol<br/>1972]
E[TCP/IP Flag Day<br/>January 1983]
A --> B --> C --> D --> E
这里最重要的是不要把箭头理解成严格的“RFC 1 直接升级成 TCP”。
中间经历了大量协议重新设计。
RFC 1 更像是起点之一。
二、RFC 这种开放讨论机制本身成为了更大的遗产
RFC 1 最有历史意义的地方,也许甚至不是 Host Software。
而是:
1 | |
这套工作方式本身。
作者没有假装方案已经完善,而是把:
- 已知设计;
- 暂定共识;
- 尚未解决的问题;
- 实验计划;
一起公开。
RFC 2 很快就针对 RFC 1 的建议继续讨论。
于是“文档 → 评论 → 修改 → 新文档”的技术协作方式开始形成。
后来整个 Internet Engineering 的标准文化,都深受这种机制影响。
三、它展示了互联网早期最重要的工程方法:先互通,再演化
RFC 1 的很多方案都很朴素:
- 用 TTY 模拟远程终端;
- 固定 32 条 Link;
- Host ID 决定冲突;
- Link 0 做控制;
- 为文件传输再开一条 Link;
- 高延迟就把前端程序下载到本地。
这些设计今天看来并不漂亮。
但它们有一个共同特点:
足够简单,能够被当时不同机器、不同操作系统的团队实现和验证。
网络协议如果永远追求理论上的完美,却不能让不同系统尽快互操作,那么它就无法形成网络效应。
RFC 1 非常鲜明地体现了这种工程文化:
1 | |
这可能比 RFC 1 中任何一个具体 bit field 都更值得今天的软件开发者学习。
总结
RFC 1 并不是互联网协议栈的“第一块最终标准”。
它更像互联网协议工程的第一张草图。
它面对的是一个非常基础、但非常困难的问题:
底层网络已经能传数据之后,异构主机之间到底怎样建立真正可用的软件通信?
围绕这个问题,RFC 1 给出了一批早期答案:
- Host 之间通过 Message 通信,单个 Message 最大 8080 bits;
- Message Header 为 16 bits,包含
Destination 5 bits、Link 8 bits、Trace 1 bit、Spare 2 bits; - IMP 把 Message 拆成最大 1010-bit Packet;
- Packet 使用 24-bit cyclic checksum;
- 每对 Host 有 32 条逻辑全双工 Link;
- RFNM 控制同一 Link 上发送下一条 Message 的时机;
- Link 0 被保留用于 Host OS 间的连接控制;
- 普通交互采用 TTY-like Connection;
- 大数据传输使用独立的 File-like Connection;
- Host Software 应为用户程序提供明确的连接、收发、关闭 primitives;
- 即使网络内部已经校验,Host 仍提出 Host-to-Host Error Checking;
- 为解决约 0.5 秒的远程交互延迟,提出 DEL,把简单终端控制逻辑下载到本地运行;
- 最初实验计划围绕 SRI 的 NLS 展开,并进一步扩展到图形交互。
其中绝大多数具体协议机制今天已经废弃。
RFC 1 在 1971 年的 RFC 100 中已经被标记为 Obsolete;Host-to-Host 协议随后继续演化,ARPANET 后来采用 NCP,并最终在 1983 年 1 月切换到 TCP/IP。
但如果把已经过时的协议细节剥掉,RFC 1 留下的问题依然非常现代:
1 | |
这就是 RFC 1 最值得读的地方。
它不是在告诉我们今天应该怎样实现网络。
它是在展示:
互联网最早的一批工程师,是怎样开始学习“设计网络协议”这件事的。
如果继续沿着 RFC 编号往后读,会发现很多今天习以为常的网络概念,并不是一开始就设计完整的,而是在一次次 RFC 的讨论、实验、失败、修订和替代中逐渐长出来的。
RFC 1,就是这场长期技术对话的第一声。