RFC 9 带读:Host Software——从 Logical Link 到 Service Call,早期 Host-Host 协议开始进入操作系统

前言

RFC 9 的标题再次回到一个看起来很普通的名字:Host Software

但如果把它和前面的 RFC 1、RFC 2、RFC 7、RFC 8 放在一起看,会发现它已经明显进入了另一个阶段。

RFC 1 还在讨论:

1
Host Software 大概应该做什么?

RFC 7 开始讨论:

1
2
Network Program、Handler、Buffer
在 Host 里应该怎么组织?

RFC 8 则把:

1
2
3
4
5
6
7
Checksum
Link
TTY
File Transfer
DEL
Network Program
Handler

重新整理成一套阶段性的功能规格。

到了 RFC 9,Gérard Deloche 把重点进一步推向:

Host-Host Protocol 到底怎样映射成操作系统可以调用、可以管理、可以存储状态的一套软件结构?

所以 RFC 9 最值得软件开发者读的地方,不是它又一次定义了 Link,而是它第一次比较系统地把三层东西连了起来:

1
2
3
4
5
6
7
Host-Host Protocol

Network Service Calls

HOST / LINK / USER Data Structures

Network Program

这已经非常接近今天我们熟悉的:

1
2
3
4
协议
→ API
→ 内核对象 / 状态表
→ 网络栈实现

RFC 9 还把此前的 32 条 Link 扩展成:

1
256 logical links

并明确区分:

1
2
3
4
5
6
7
8
Link 0
→ Control Link

Primary Link
→ 首条 TTY-like 交互链路

Auxiliary Link
→ 与 Primary Link 并行的大数据链路

同时,它给出了:

1
2
3
4
5
OPENPRIM
OPENAUX
TRANSLINK
MODIFLINK
CLOSSLINK

一组非常早期的 Network Service Calls(网络服务调用)

也就是说,一个普通 User Program 不再需要理解:

1
2
3
4
5
IMP
Link 0 控制消息
网络 Link ID
ACK / NAK
建链细节

而可以通过操作系统提供的服务接口来请求:

1
2
3
4
5
打开主连接
打开辅助连接
发送数据
修改连接
关闭连接

这就是 RFC 9 真正的变化:

网络正在从“硬件设施”变成“操作系统提供给应用程序的公共服务”。

不过,这套设计并没有活很久。

1971 年的 RFC 100 对早期 Host/Host Protocol 的总结非常明确:

1
2
3
4
5
6
7
8
9
10
11
RFC 9、RFC 11、RFC 22
代表一次早期 Host/Host Protocol 尝试。

RFC 11 supersedes RFC 9。

这套协议能力不足,
因为它无法在不登录远端 Host 的情况下
支持 inter-host communication。

因此在 1969 年 12 月的网络会议后
整套方案被放弃。

随后 RFC 33 会建立一套更成熟的:

1
2
3
4
NCP
Connection
Socket
Control Command

模型。

所以今天阅读 RFC 9 的正确姿势不是“学习一套旧协议”。

而是观察:

一个网络接口怎样从 Link 和控制字符,逐渐长成应用可以调用的操作系统网络 API。

RFC 基本信息

项目 内容
RFC RFC 9
官方英文标题 Host Software
作者 G. Deloche(Gérard Deloche)
所属机构 UCLA
原文日期 1 May 1969
RFC Editor 当前状态 Unknown
Stream Legacy
后续内容关系 RFC 11 supersedes RFC 9
所属历史阶段 早期 Host/Host Protocol
RFC Editor 信息页 RFC 9: Host Software
RFC 原文 RFC 9 原文

RFC 9 的日期比 RFC 8 还早

RFC 9 手稿首页是:

1
1 May 1969

而 RFC 8 是:

1
5 May 1969

所以再次可以看到:

1
2
3
RFC Number

严格时间顺序

早期 RFC 的编号与起草、完成、分发、编号分配并不是今天软件版本号那样严格同步。

RFC 9 的历史状态不是简单一句 Obsolete

1971 年的 RFC 100 在索引中把 RFC 9 标记为:

1
C.1 C.2

也就是它仍然被归类到 Host/Host Protocol 的连接建立、终止与控制消息相关主题中。

但 RFC 100 在正文里又明确说明:

1
2
3
4
5
6
RFC 9、11、22
是早期 Host/Host Protocol 的一次尝试。

RFC 11 supersedes RFC 9。

这套协议最终在 1969 年 12 月被整体放弃。

因此今天比较准确的理解是:

1
2
3
4
5
RFC 9

被 RFC 11 的实现/协议描述取代

RFC 11 后来被 RFC 33 正式 Obsolete

RFC 9 同样来自早期扫描材料

RFC Editor 当前把 RFC 9 作为:

1
PDF with Images

展示。

当前网页上的全文内容来自对扫描页的整理和转写,其中一些图形和控制字符示例仍然比较难读。

因此本文只对原文明确可以确认的字段、流程、API 和数据结构做解释。

对于 Figure 3 中某些已经难以准确辨认的控制字符,不会为了“补齐协议”擅自创造定义。

为什么会有这篇 RFC

在 RFC 8 中,我们已经看到:

1
2
3
4
5
Link 0
→ Control

Other Links
→ TTY-like / File-like

用户通过远程登录,再启动应用。

但真正写一个应用程序时,还有一堆非常现实的问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
我要建立连接,
是自己发 Link 0 控制字符吗?

Link 号谁来选?

远端 ACK 谁等?

数据收到了以后,
操作系统怎么知道交给哪个 User Program?

一个用户同时开多个 Link,
状态放哪里?

一个 Host 同时和很多 Host 通信,
空闲 Link 怎么查?

如果这些事情全部让应用自己做,那么:

1
2
3
每个应用
都得重新实现
Host-Host Protocol

这显然不可接受。

所以 RFC 9 的目标之一,就是把这些复杂度收进:

1
Network Program

再向用户提供:

1
Service Calls

二、网络开始成为操作系统的公共能力

RFC 9 在 Service Calls 一章明确说:

User Program 通过:

1
executing a service call

访问网络设施。

服务调用会:

1
trap to a monitor service routine

由 Monitor 解释并执行,然后把控制权返回用户程序。

这个结构可以画成:

sequenceDiagram
    participant U as User Program
    participant M as Monitor Service Routine
    participant N as Network Program
    participant I as IMP / Network

    U->>M: Service Call
    M->>N: Execute Network Operation
    N->>I: Host-Host / IMP Interaction
    I-->>N: Result / Control
    N-->>M: Status
    M-->>U: Return

这已经非常明确地表现出:

1
2
Application
不能直接操作 Network Hardware

而必须通过:

1
OS Service Interface

RFC 8 还是:

1
32 Links

RFC 9 已经改成:

1
256 Logical Links

这不是简单把字段变大。

它马上带来:

1
2
3
4
5
6
7
8
哪个 Link 空闲?
哪个正在使用?
属于哪个远端 Host?
属于哪个用户?
Primary 还是 Auxiliary?
谁打开的?
是否已经真正建立完成?
对应哪个 Buffer?

所以 RFC 9 必须引入:

1
2
3
HOST Table
LINK Table
USER Table

换句话说:

协议状态一旦变复杂,数据结构设计就会成为协议实现的一部分。

四、连接模型也进一步从“Link”变成“用户资源”

RFC 9 明确区分两种 ID:

1
User Link Identification

和:

1
Network Link Identification

也就是说:

1
应用看到的 Link ID

不一定等于:

1
网络内部真正使用的 Link Number

这非常重要。

它开始建立一个典型的软件抽象:

1
2
3
4
5
User Handle

OS / Network Program Mapping

Network Resource

现代开发者对此非常熟悉:

1
2
3
4
5
file descriptor
socket handle
channel id
stream id
request handle

RFC 9 已经开始沿着这个方向走。

核心内容

RFC 9 的目录包括:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1. Introduction

2. HOST-HOST Protocol
2.1 Logical Links
2.1.1 Primary
2.1.2 Auxiliary
2.2 Link Establishment
2.2.1 General Procedures
2.2.2 Example

3. Network Service Calls
3.1 List of Service Calls

4. Data Structure
4.1 HOST Table
4.2 LINK Table
4.3 USER Table

5. Network Program

整体结构非常像今天一份网络子系统设计文档:

flowchart TD
    P[Protocol Model]
    A[Application API]
    D[Kernel / Monitor Data Structures]
    I[Network Program Implementation]

    P --> A
    A --> D
    D --> I

一、IMP 被看成 Local Center 与 Trunk Network 的接口

RFC 9 先从 IMP 讲起。

原文说:

1
2
3
4
Any IMP can be viewed as
an interface between
a local center
and the trunk network.

本地一个 IMP 最多可以服务:

1
4 HOSTs

而对于其中每一个 Host:

1
2
提供到任意远程 Host 的
256 logical links

因此从 Host 的视角:

1
2
3
4
5
6
7
Local Host

Local IMP

256 logical paths

Remote Host

二、256 Logical Links,但并不是全都能同时激活

RFC 9 紧接着指出:

1
2
3
between an IMP and all the other IMPs
no more than 64 links
may be in use simultaneously

也就是说:

1
2
3
4
5
逻辑命名空间
= 256

同时可激活资源
≤ 64

如果一个 Local Center 有:

1
n HOSTs

则这:

1
64 active links

需要在:

1
n HOSTs

之间共享。

这非常值得注意。

因为它已经明确区分:

1
Logical Address Space

和:

1
Physical / Runtime Capacity

现代系统也经常如此:

1
2
3
4
5
Stream ID 可以很多
但真正并发资源有限

Connection ID 可以很多
但 Socket / Memory / Queue 有上限

256 条 Link 里:

1
Link 0

有特殊地位。

用于:

1
2
3
Connection Requests
Status Reports
其他 Control Information

其余:

1
255 Links

分成:

1
2
Primary
Auxiliary

两种使用方式。

RFC 9 明确说:

1
2
3
A primary link
is the first link established
for a HOST-HOST transmission.

它是:

1
TTY-like connection

具有这些特征:

1
2
3
4
5
6
ASCII characters are transmitted
Remote HOST generates echo
Remote HOST scans for break character
Transmission < 20 characters / second
Mainly used for control commands
Special buffering for slow short transmissions

这里最重要的一个数字是:

1
less than 20 characters per second

这让 Primary Link 的定位非常明确:

它本质上就是面向人类终端交互的低速控制通道。

RFC 9 明确举例:

1
log-in to the remote HOST operating system

也就是说:

1
Primary Link

首先不是高吞吐数据连接,而是:

1
用户先进入远端 OS

的交互入口。

流程:

1
2
3
4
5
6
7
Local User

Primary Link

Remote OS Login

Remote Application

这也直接埋下了这套协议后来被放弃的核心原因。

因为:

如果所有 inter-host communication 都依赖“先登录远端 Host”,就很难支持真正机器到机器的自动进程通信。

RFC 100 后来正是因此批评这套协议“不够强”。

六、Auxiliary Link:大容量数据传输

Auxiliary Link 用于:

1
large volumes of data

它:

1
与 Primary Link 并行建立

并且必须满足:

1
2
两端 User Program
都要求打开它

同时可以传:

1
2
3
binary

character

所以:

flowchart LR
    A[HOST X]
    B[HOST Y]

    A <-->|Primary<br/>TTY / Control| B
    A <-->|Auxiliary<br/>Bulk Binary / Character Data| B

这比 RFC 8 的:

1
TTY-like / File-like

命名更系统一些。

七、Primary / Auxiliary 已经形成“控制 + 数据”的组合

一个典型远程应用会:

1
先通过 Primary Link 登录和控制

然后:

1
2
再打开 Auxiliary Link
传大块数据

所以整个 Session 不再是一条 Link。

而是一组有角色关系的 Link。

这已经开始从:

1
link == session

转向:

1
2
session
由多个 logical links 共同构成

RFC 9 定义:

通过:

1
Control Link 0

建立 Primary Link。

建立成功后:

1
Connection = Pre-login State

远端 Host 等待标准 Login Procedure。

2. Login Sequence

通过 Primary Link:

1
send / receive standard ASCII characters

完成远端登录。

需要:

1
两端共同执行

仍然通过:

1
Control Link 0

完成控制。

之后批量数据才走:

1
Auxiliary Link

九、RFC 9 的示例使用 HOST ID 8 和 5

原文示例设:

1
2
HOST(X) network ID = 8
HOST(Y) network ID = 5

这个数字后面用于决定:

1
2
如果两边同时动作
由谁主动建立 Auxiliary Link

由于:

1
8 > 5

所以:

1
HOST(X)

负责主动建立。

这延续了前面 RFC 中:

1
用 Host ID 作为确定性 Tie-Breaker

的做法。

RFC 9 的 Figure 3 给出了一个具体建链示例。

其中可以明确识别:

1
ENQ

用于:

1
Enquiry for Link Establishment

随后消息中包含:

1
PRIM

表示:

1
Link Type = Primary

以及:

1
Link Identification Number

和:

1
Option

远端返回:

1
ACK

表示:

1
Positive Acknowledgement

确认 Link 建立。

也可能返回:

1
NAK

表示:

1
Negative Acknowledgement

RFC 9 还说明:

1
2
NAK 可以携带一个字符
说明为什么拒绝。

这已经很像一个非常原始的:

1
2
3
4
5
6
7
Control Request
+
Parameters
+
ACK / NAK
+
Reason

协议。

十一、连续 NAK 还会触发 Emergency Procedure

RFC 9 说明:

如果某个请求被 NAK:

1
HOST(X)

可以重复发送消息:

1
until HOST(Y) accepts it

如果连续发生太多次 NAK,则:

1
an emergency procedure

会启动。

原文扫描件中关于具体阈值和某些细节不够清晰,因此不能把它写成一个现代精确重试算法。

但可以确认的是:

RFC 9 已经开始明确考虑“请求失败—重试—连续失败升级处理”的控制流。

RFC 9 的例子中:

1
HOST(X)

在登录远端以后请求一个应用程序:

1
URSA

原文说明:

1
2
3
URSA is supposed to be
a user application program
in HOST(Y)

随后双方建立:

1
Auxiliary Link 25

然后:

1
2
3
4
5
6
7
HOST(X)
→ Link 25
→ 发送一个 "file"

HOST(Y)
→ Link 25
→ 回一个 "file"

这说明 Auxiliary Link 的典型用途就是:

1
application bulk exchange

会话结束时:

1
HOST(X)

释放之前建立的 Link。

RFC 9 示例里可以辨认出:

1
End of transmission

相关控制内容。

也就是说:

1
Link

是一种需要明确:

1
2
3
4
5
allocate
establish
use
close
free

的资源。

关键机制与工作流程

RFC 9 真正比 RFC 8 更进一步的地方,是:

它开始把协议过程直接暴露成操作系统 API。

一、Network Service Call 是怎样工作的

原文说明:

1
2
3
A user program accesses
the network facilities
by means of executing a service call.

服务调用:

1
traps to a monitor service routine

由 Monitor 完成操作,再:

1
2
control is then given back
to the user program

流程:

sequenceDiagram
    participant U as User Program
    participant M as Monitor
    participant N as Network Program
    participant R as Remote HOST

    U->>M: Network Service Call
    M->>N: Parse / Execute
    N->>R: Host-Host Protocol
    R-->>N: Control / Data
    N-->>M: Result
    M-->>U: Return / Interrupt / Status

这就是:

1
System Call Boundary

的早期形态。

RFC 9 给出的第一类调用是:

1
OPENPRIM

可以从原文辨认出的主要参数包括:

1
2
3
4
5
PRIMID
HOSTID
BUFFADDR
INTPT-CODE
OPT

可以整理为:

参数 含义
PRIMID 用户给这条 Primary Link 的标识
HOSTID Remote Host ID
BUFFADDR Incoming Message Buffer 地址
INTPT-CODE 收到消息导致用户程序被中断时返回的 Code
OPT 可选参数

OPT 可能包括:

1
2
3
full echo
建立成功后是否要求消息
其他选项

这里非常值得注意:

用户自己提供一个 PRIMID,而 Network Program 内部会再分配真正的 Network Link ID。

也就是说:

1
2
3
User-level ID

Network-level ID

第二个调用:

1
OPENAUX

原文可以确认的核心参数包括:

1
2
3
4
5
AUXID
PRIMID
BUFFADDR
INTPT-CODE
OPT

其中:

1
PRIMID

引用:

1
already established Primary Link

所以 Auxiliary Link 不是完全独立资源。

它属于某个已有 Primary Link 的上下文。

可以理解成:

1
2
3
4
Primary Session
├── Auxiliary A
├── Auxiliary B
└── Auxiliary C

RFC 9 给出:

1
TRANSLINK(ID, BUFFADDR, N, [OPT])

含义大致是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ID
→ 用户的 Link 标识

BUFFADDR
→ 数据所在内存地址

N
→ 发送的 byte 数量

OPT
→ binary / character
acknowledgement
trace
等选项

这个形式已经非常像现代:

1
send(handle, buffer, length, options)

RFC 9 还定义:

1
MODIFLINK(ID, OPT)

用于修改已经存在 Link 的一些行为。

虽然原文没有发展成现代成熟 Option Model,但它表达了一个非常重要的 API 思路:

连接建立后仍然可以调整其运行属性。

还有:

1
CLOSSLINK(ID, [OPT])

可以关闭:

1
指定 Link

并且某些 Option 可以:

1
2
close all the links
in use by the user

这已经是明确的:

1
Resource Cleanup API

七、这些 Service Calls 已经构成一个早期 Network API

如果把原文压缩成现代伪接口:

1
2
3
4
5
6
7
8
9
openPrimary(userId, remoteHost, inputBuffer, interruptCode, options)

openAuxiliary(auxId, primaryId, inputBuffer, interruptCode, options)

transmit(linkId, buffer, length, options)

modify(linkId, options)

close(linkId, options)

非常值得强调:

这不是说 RFC 9 发明了 BSD Socket API。

两者没有直接继承关系。

但从软件抽象角度看,RFC 9 已经非常清楚地走向:

1
2
3
4
5
资源创建
→ 使用 Handle
→ 数据传输
→ 修改属性
→ 关闭资源

这种系统 API 模式。

八、协议状态需要三张表管理

RFC 9 的 Data Structure 一章特别重要。

它说:

1
2
allocation and management of links
are carried out by means of three tables

分别是:

1
2
3
HOST Table
LINK Table
USER Table

这三张表分别从三个查询维度组织状态。

HOST Table 是:

1
bit-table

对于一个给定 Remote Host:

1
256 bits

分别表示:

1
256 logical links

其中:

1
2
3
bit 0
=
free link

如果 Link 已经占用:

1
bit = 1

于是:

1
2
HOST 5:
000100100...

就能快速表示:

1
2
哪些 Link 已用
哪些空闲

更重要的是,RFC 9 还要求:

1
2
3
整个表中
同时最多 64 个 bit
可以为 1

对应 IMP 的实际并发 Link 限制。

十、这是一个非常早的 Bitmap Resource Allocator

从现代视角:

1
2
256 个资源槽
每个用 1 bit 表示 free / used

就是非常典型的:

1
Bitmap Allocator

查找第一个:

1
0 bit

就可以找到:

1
First Free Link

LINK Table:

1
2
contains as many sections
as links in use

也就是说它不是为全部 256 × Host 预分配完整对象。

只为:

1
Active Links

创建条目。

RFC 9 还说明:

1
2
3
4
starting and retrieval
are carried out dynamically
using hashing
based on network link identifications

换句话说:

1
2
3
Network Link ID
→ Hash
→ Link State Entry

已经是一种典型的运行时状态索引结构。

USER Table:

1
one section per active user

包含:

1
2
3
User Identification
+
Link IDs used by this user

并且:

1
hashed by users

于是三张表分别支持:

1
2
3
按 Host 查资源
按 Link 查状态
按 User 查所有连接

可以画成:

flowchart TD
    H[HOST Table<br/>Remote HOST → Free/Used Link Bitmap]
    L[LINK Table<br/>Network Link → Runtime State]
    U[USER Table<br/>User → Link Set]

    H --> L
    U --> L

RFC 9 特别强调:

1
a link has two identifications

User Identification

由用户在:

1
OPEN Service Call

中传入。

Network Identification

由:

1
Network Program

分配。

这可以表示为:

1
2
3
4
5
6
7
8
User:
AUXID = 7


Network Program Mapping


Remote HOST 5 / Network Link 25

这已经是非常明确的:

1
2
Logical Handle
→ Physical / Protocol Resource

映射。

十四、OPENPRIM 内部到底怎么执行

RFC 9 最后还具体解释:

1
OPENPRIM

的内部过程。

假设:

1
HOSTID = j

第一步:HOSTID 作为 HOST Table Index

1
2
3
j
→ HOST Table
→ HOST j section

搜索:

1
first bit = 0

设找到:

1
Link i
1
j + i

共同确定:

1
2
3
Remote Host
+
Network Link Number

用:

1
j-i

作为 Hash 输入,创建新的 LINK Table Section。

第五步:先标记“我们已经发起打开”

条目里会记录:

1
2
Link opened by us
Primary

但此时:

1
还不能算 fully established

RFC 9 明确说:

只有当:

1
收到 Remote HOST 的 acknowledgement

以后:

1
the link is considered completely established

然后才设置:

1
Link Established Bit

这个细节非常重要。

状态不是:

1
2
OPEN called
→ immediately OPEN

而是:

1
2
3
4
Allocated
→ Request Sent
→ ACK Received
→ Established

可以画成:

stateDiagram-v2
    [*] --> Free
    Free --> Allocated: 选择空闲 Link
    Allocated --> OpenRequested: 发送 Link 0 Request
    OpenRequested --> Established: Remote ACK
    OpenRequested --> Failed: NAK / Error
    Established --> Closing: CLOSSLINK
    Closing --> Free: Release

十六、RFC 9 已经在数据结构里保存协议状态机

LINK Table 中不是只存:

1
Link Number

还要保存:

1
2
3
4
5
谁打开的
Primary / Auxiliary
是否 Established
Buffer Address
User ID

这意味着:

协议状态机最终一定要落到某种可查询的运行时状态对象里。

这是 RFC 9 非常现代的一点。

重点概念与术语

HOST-HOST Protocol

两个 Host 之间用于建立连接、传输数据、维护状态和终止连接的协议。

注意:

RFC 9 属于一套非常早期的 Host-Host Protocol。

它不是后来成熟的 NCP 最终协议。

IMP 向 Host 暴露的逻辑通信路径。

RFC 9:

1
256 logical links

但最多:

1
64 simultaneously active
1
Link 0

专门用于:

1
2
3
Connection Request
Status
Control Message

Host-Host 会话建立的第一条 Link。

特点:

1
2
3
4
5
6
TTY-like
ASCII
Remote Echo
Break Scan
< 20 chars/sec
Control / Login

与 Primary Link 并行建立的额外数据 Link。

用于:

1
2
3
large volume
binary
character

Pre-login State

Primary Link 已经建立,但远端 OS 登录尚未完成的状态。

ENQ

Enquiry

RFC 9 示例中用于发起 Link Establishment 的控制字符之一。

ACK

Positive Acknowledgement

表示控制请求被接受。

NAK

Negative Acknowledgement

表示请求被拒绝。

RFC 9 还允许伴随原因字符。

HOST Identification Number

Host 的网络编号。

RFC 9 用它作为:

1
2
冲突处理
主动建链

的确定性排序依据。

Network Service Call

User Program 向 Monitor / Network Program 请求网络功能的系统级调用。

OPENPRIM

打开 Primary Link 的 Service Call。

OPENAUX

基于已有 Primary Link 打开 Auxiliary Link。

在指定 Link 上发送数据。

修改 Link 的 Option / 行为。

关闭一条或一组 Link。

Monitor Service Routine

处理 User Program Service Call 的操作系统特权例程。

HOST Table

按 Remote Host 组织:

1
256-bit Link Allocation Bitmap

存储所有:

1
Active Link Runtime State

并通过 Network Link ID Hash 定位。

USER Table

以:

1
User ID

为索引,记录该用户正在使用的 Link。

用户程序自己使用的逻辑标识。

Network Program 实际分配的网络 Link 标识。

Hashing

RFC 9 明确使用 Hash 技术来动态检索:

1
2
LINK Table
USER Table

这说明网络状态数据结构已经开始考虑:

1
快速查找

而不只是线性扫描。

URSA

RFC 9 示例中假设存在于 HOST(Y) 的:

1
User Application Program

它只是示例应用名。

不应把它当成后来互联网标准协议。

需要特别注意的点

1. RFC 9 不是后来正式 NCP 的最终协议

这是最重要的一点。

RFC 100 明确说:

1
2
3
RFC 9、11、22
represent an early attempt
at a Host/Host protocol.

这套协议后来被整体放弃。

2. RFC 11 supersedes RFC 9

RFC 100 明确记录:

1
11 supersedes 9

RFC 11 会更完整地把:

1
Host-Host Protocol

和 UCLA 的:

1
GORDO Operating System

结合起来。

所以 RFC 9 更像:

1
设计阶段

RFC 11 更接近:

1
完整实现说明

3. RFC 11 后来又被 RFC 33 正式废弃

RFC Editor 当前对 RFC 11 明确标注:

1
Obsoleted by RFC 33

RFC 33:

1
New HOST-HOST Protocol

代表 1969 年 12 月网络会议后的一次大重构。

4. 旧协议被放弃的核心原因非常重要

RFC 100 对这套协议失败原因写得非常清楚:

1
2
3
it didn't provide for
inter-host communication
without logging in

也就是说:

Host A 的程序如果想和 Host B 的程序通信,模型高度依赖“先像一个 TTY 用户一样登录 Host B”。

这对于:

1
人 → 远程计算机

还算自然。

但对于:

1
Program A → Program B

就过于受限。

这是早期网络从:

1
Remote Terminal Network

转向:

1
Interprocess Communication Network

的一个关键转折。

Primary Link 的远端 Echo、Break Character、Login 和 <20 chars/s 都带有强烈的 TTY 语义。

不能拿 TCP 的通用字节流直接替换理解。

它确实有:

1
2
3
Primary
+
Auxiliary

两类 Link。

很容易联想到后来 FTP 的:

1
2
3
Control Connection
+
Data Connection

但历史上不能直接说:

1
FTP 就来源于 RFC 9。

只能说两者都面对:

1
2
交互控制和批量数据
具有不同传输需求

这一类共同问题。

RFC 9 明确:

1
256 logical links available

但:

1
2
最多只有 64
simultaneously active

所以:

1
2
3
Addressable

Active Capacity

如果一台 IMP 挂:

1
n Hosts

那么:

1
64

是这些 Host 一起共享的。

这意味着:

IMP 是一个真实的共享受限资源,不是无限能力的逻辑交换机。

9. RFC 9 的 ACK / NAK 不是现代 TCP ACK

这些:

1
ACK / NAK

是 Host-Host Control Message 的协议控制字符。

和:

1
RFNM

不是一个层次。

更和 TCP Sequence ACK 不同。

10. RFNM 仍然是 IMP 层的 Flow Control

虽然 RFC 9 重点不在 RFNM,但 Network Program 仍负责:

1
receiving RFNM control messages

必须继续区分:

1
2
3
4
5
RFNM
→ IMP 网络反馈

ACK
→ Host-Host Control Protocol 确认

这是 RFC 9 最值得注意的软件设计细节之一。

不要假设:

1
AUXID = 7

就表示:

1
Network Link 7

Network Program 会做映射。

12. HOST Table 的 256-bit Bitmap 只是当时实现方案

不要把它理解成协议线上格式。

它属于:

1
Host Software Data Structure

而不是:

1
Message Header

13. Hash Table 也属于实现层

RFC 9 对:

1
2
LINK Table
USER Table

提出 Hash。

这是:

1
实现建议 / 软件结构

不是远端 Host 必须遵循的 wire protocol。

14. Service Call 名称不是现代标准 API

1
2
3
4
5
OPENPRIM
OPENAUX
TRANSLINK
MODIFLINK
CLOSSLINK

是 RFC 9 这套设计的接口。

它们不是 POSIX,也不是后来 BSD Socket。

15. OPEN 被调用以后并不代表网络状态已经完全建立

RFC 9 的内部描述强调:

1
2
ACK 回来以后
才设置 Established Bit

所以:

1
2
3
资源已分配

远端已确认

这个区别很重要。

16. 原文 Figure 3 的某些控制字符不宜过度解码

当前扫描和机器转写中:

1
2
3
4
ENQ
PRIM
ACK
NAK

等核心含义可以确认。

但部分括号字符、ASCII 数字表达和 Option 示例已经不够清晰。

写博客时保留可以验证的协议结构即可。

17. URSA 是示例,不是标准应用协议

RFC 9 只是说:

1
2
URSA is supposed to be
a user application program

不能围绕它脑补一套不存在的标准。

18. 这套协议最大的问题不是“字段设计不漂亮”,而是抽象层次错了

RFC 100 并没有说:

1
2
3
Link 太少
Header 太小
ACK 不够好

而是指出:

1
不能不登录就做 Host 间通信

这说明真正导致协议被推倒重来的,是:

通信模型本身不够通用。

从今天的视角重新看这篇 RFC

一、RFC 9 很像“网络 API 从零长出来”的现场记录

如果只看前面的 RFC,网络还是:

1
2
3
4
Link
Message
IMP
Control

RFC 9 开始出现:

1
2
3
4
open
transmit
modify
close

这已经是应用开发者熟悉的资源 API。

一个网络能力只有变成:

1
Application-callable Interface

才真正能被大规模软件使用。

二、协议对象应该有稳定的 User Handle

RFC 9 的双 ID:

1
2
User Link ID
Network Link ID

是很现代的抽象。

为什么不能把底层 Network Link ID 直接给应用?

因为底层资源:

1
2
3
可能重新分配
可能变化
属于协议内部实现

应用更适合看到:

1
自己的稳定句柄

现代很多系统都这样:

1
2
3
4
5
fd
handle
channel
stream object
connection object

三、API 和协议不应该是一回事

User Program 调:

1
OPENPRIM

Network Program 内部却要做:

1
2
3
4
5
6
7
查 HOST Table
分配 Network Link
更新 LINK Table
更新 USER Table
通过 Link 0 发控制消息
等待 ACK
最后设置 Established

这非常清晰地说明:

1
2
3
API
=
对复杂协议流程的抽象

而不是:

1
让应用自己发协议帧。

四、一个 API Call 背后可能是异步状态机

OPENPRIM 看起来只是一个函数。

实际却经历:

1
2
3
4
5
Free
→ Allocated
→ Request Sent
→ Await ACK
→ Established

现代网络编程也是一样:

1
connect()

表面是一句代码。

背后可能涉及:

1
2
3
4
5
6
DNS
ARP / ND
TCP Handshake
TLS Handshake
Proxy
Authentication

软件接口的价值之一就是隐藏这些过程。

五、资源管理需要多个索引视角

RFC 9 用:

1
2
3
HOST Table
LINK Table
USER Table

分别支持不同查询。

这是一种非常成熟的数据建模意识。

现代网络服务也经常同时需要:

1
2
3
4
5
6
By User
By Connection
By Remote Peer
By Tenant
By Stream
By Session

一个巨大 Map 往往不能高效支持所有访问模式。

六、Bitmap 很适合有限 ID 空间的 Resource Allocation

RFC 9 的:

1
2
256 Link
→ 256-bit Bitmap

是一种极自然的数据结构。

今天仍会用在:

  • Port Pool;
  • CPU Affinity;
  • Memory Page;
  • ID Slot;
  • File Descriptor Set;
  • Resource Flags。

如果资源 ID 空间:

1
2
3
有限
固定
连续

Bitmap 通常非常高效。

七、Hash Table 适合稀疏 Active State

另一方面:

1
256 × 所有 Host

虽然 ID 空间大,但真正 Active Link 只有少数。

所以 LINK Table 只为:

1
Links in use

创建 Section,并使用 Hash 查找。

这和:

1
2
3
Dense Namespace
+
Sparse Active Objects

的现代实现思路完全一致。

八、Logical Capacity 与 Physical Capacity 必须分开建模

RFC 9:

1
256 Link IDs

但同时:

1
Only 64 Active

这是一个非常重要的资源设计模式:

1
2
3
Identifier Space

Concurrency Limit

现代系统也会区分:

1
2
3
4
5
6
7
8
最大 Request ID
最大并发 Request

最大 Stream ID
最大 Active Stream

最大 Session Namespace
最大 Worker Capacity

九、Control Plane 与 Data Plane 分离又向前走了一步

RFC 9:

1
2
3
4
5
Link 0
→ 建链 / 状态 / 控制

Primary / Auxiliary
→ 用户交互 / 数据

再加上:

1
2
3
Service Call
→ Network Program
→ Control Message

可以看到控制路径越来越集中。

这种结构在系统复杂以后几乎是必然的。

十、但“所有通信先 Login”暴露了过度绑定人类交互模型的问题

这是 RFC 9 最值得现代软件架构师反思的一点。

最初需求是:

1
用户远程使用别人的计算机

于是协议自然设计成:

1
2
3
4
建 TTY
→ Login
→ 启动程序
→ 再传数据

但当网络用途扩展成:

1
Program-to-Program

这个模型马上变得很笨重。

这就是一个典型的:

以第一批 Use Case 为中心设计的抽象,可能无法覆盖下一代 Use Case。

十一、应该把“身份认证”与“连接建立”解耦到合适程度

RFC 9 的问题之一,是:

1
2
3
Communication
强依赖
Remote OS Login

后来网络协议演化会逐渐把:

1
2
3
transport connection
process endpoint
application authentication

分成更独立的层次。

这让机器通信不必先模拟:

1
一个人坐在 TTY 前登录远端主机。

十二、Primary / Auxiliary 模型说明一个 Session 可以由多个 Stream 构成

虽然 RFC 9 这套设计最终被放弃,但它触及了一个今天仍然重要的问题:

1
2
一个逻辑 Session
是否需要多个不同性质的通信流?

现代协议中:

1
2
3
4
5
HTTP/2 Streams
QUIC Streams
SSH Channels
Media Tracks
RPC Substreams

都会处理类似需求。

区别是现代协议把这种 Multiplexing 做得更加通用。

十三、状态机应该落到数据结构,不应该只存在文档里

RFC 9 中:

1
2
3
4
5
Link opened by us
Primary
Established
Buffer Address
User ID

这些字段表明:

1
协议状态

必须被编码成:

1
运行时状态对象

否则程序无法:

1
2
3
4
恢复
查询
匹配 ACK
处理 Close

这对今天设计任何有状态协议都成立。

十四、网络功能一旦进入 OS,就必须有权限和生命周期边界

RFC 9 的 Service Call 通过:

1
trap to monitor

进入特权层。

这说明网络资源不是普通用户程序随便操纵的硬件。

操作系统需要管理:

1
2
3
4
Who owns link?
Which user?
Which buffer?
When close?

今天 Socket / Network Namespace / Capability / Firewall 都是在更成熟地回答类似问题。

十五、RFC 9 的失败本身比成功更值得学习

RFC 9 最大的价值之一,是它后来真的被推翻了。

因为它证明:

协议设计不能只问“能不能实现”,还要问“抽象能不能支撑未来用途”。

这套协议当然能实现远程登录和数据传输。

但它不能优雅支持:

1
无需 Remote Login 的 Host-to-Host Process Communication

于是整个模型被重新设计。

这是非常典型的软件架构演化。

这篇 RFC 带来的影响

一、RFC 9 让 Host-Host Protocol 第一次和明确 API 绑定起来

前面的文档更多是:

1
2
我们应该有 Link
我们应该能建 Link

RFC 9 则真正写:

1
2
3
4
5
OPENPRIM
OPENAUX
TRANSLINK
MODIFLINK
CLOSSLINK

从这里开始,协议能力已经不再只是:

1
wire behavior

而是:

1
programming interface

二、RFC 11 直接继承并扩展这套思路

RFC 100 明确说:

1
RFC 11 supersedes RFC 9

RFC 11:

Implementation of the Host-Host Software Procedures in GORDO

把这些设计进一步整合进:

1
UCLA GORDO Operating System

并详细讨论:

  • Links;
  • Connections;
  • Message Structure;
  • Transactions;
  • Buffers;
  • Interrupt Processing;
  • Network Program;
  • Handler;
  • System Calls。

所以可以把:

1
RFC 9

看作 RFC 11 的直接设计前身。

三、RFC 22 又修改 Control Message Formats

RFC 100 总结:

1
2
3
4
RFC 22
contains some modifications
to control message formats
presented in RFC 11

说明:

1
Link 0 Control Protocol

仍然在快速变化。

这也是早期 RFC 的典型模式:

1
2
3
RFC 9
→ RFC 11
→ RFC 22

不断增量修正。

四、1969 年 12 月,这整套 Host-Host Protocol 被推翻

RFC 100 明确记录:

1
2
3
4
5
6
7
8
The protocol was not considered powerful enough
because it didn't provide
for inter-host communication
without logging in.

This protocol was thrown out
as a result of a network meeting
in December 1969.

这不是:

1
小版本升级

而是:

1
重新思考抽象

五、RFC 33 建立新的 Host-Host Protocol

1970 年 2 月:

1
2
RFC 33
New HOST-HOST Protocol

明确说:

1
2
3
many changes
from the old protocol
in NWG/RFC 11

并正式:

1
Obsoletes RFC 11

RFC 33 里开始出现更加成熟的:

1
2
3
4
5
6
7
NCP
Process
Connection
Socket
Control Command
System Call
Flow Control

RFC 9:

1
2
256 logical links
Primary / Auxiliary

RFC 33 则明确:

1
each link is unidirectional

并通过:

1
connection

把两个 Process 间更高层通信抽象出来。

这是一个非常关键的成熟过程:

1
2
3
Link
不再等于
用户连接

而变成:

1
底层传输资源

上层另有:

1
Connection

七、后来 Socket 进一步解决“进程端点”问题

RFC 33 把:

1
2
3
4
HOST
Process
Port / Socket
Connection

逐渐拆开。

这正是 RFC 9 的 TTY / Login 模型没有处理好的问题:

1
2
3
一个 Host 上有很多 Process
真正通信对象不是“整台 Host”
而是具体 Process Endpoint

这个方向最终会成为现代网络编程最核心的抽象之一。

八、RFC 9 仍然留下了几个很耐用的软件工程思想

虽然协议被废弃,但下面这些东西没有过时:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
把协议能力包装成 Service Call

User Handle 和 Network ID 解耦

为不同查询路径建立不同索引结构

有限 ID 空间使用 Bitmap

Sparse Runtime State 使用 Hash

连接必须有明确 Lifecycle

远端 ACK 到达以前不能假装 Established

Control 与 Data 使用不同逻辑通道

九、它展示了“失败的架构”同样值得长期保存

如果 RFC 系列只保留最终成功协议,我们就只能看到:

1
2
3
NCP
TCP/IP
HTTP

却看不到:

1
为什么旧模型不够

RFC 9 的价值恰恰在这里。

我们可以看到:

1
Remote Login-Centric Design

为什么无法自然扩展成:

1
General Interprocess Communication

这个失败过程本身就是协议史的重要知识。

总结

RFC 9 是早期 ARPANET Host Software 设计中一个非常重要的转折点。

它已经不满足于描述:

1
2
Host A 和 Host B
可以通过 Link 通信

而开始建立一个更完整的软件模型:

1
2
3
4
5
6
7
8
9
10
11
User Program

Network Service Call

Monitor / Network Program

HOST / LINK / USER Tables

Host-Host Protocol

IMP

在协议层,RFC 9 把:

1
256 Logical Links

提供给每对 Host。

其中:

1
2
Link 0
→ Control Link

其余可以作为:

1
2
3
4
5
Primary Link
→ TTY-like / Login / Control

Auxiliary Link
→ Bulk Data / Binary / Character

但 256 只是逻辑空间。

IMP 实际同时最多允许:

1
64 Active Links

而且如果本地 IMP 服务多个 Host:

1
64

还需要共享。

Primary Link 是一段 Host-Host 会话的第一条 Link:

1
2
3
4
ASCII
Remote Echo
Break Character
< 20 chars/sec

典型流程:

1
2
3
4
5
6
7
8
9
10
11
建立 Primary

Pre-login

远端 OS Login

启动 User Application

双方建立 Auxiliary

传大块数据

控制流程则通过:

1
Link 0

使用类似:

1
2
3
ENQ
ACK
NAK

的消息完成。

更重要的是,RFC 9 开始向 User Program 提供明确的网络 API:

1
2
3
4
5
OPENPRIM
OPENAUX
TRANSLINK
MODIFLINK
CLOSSLINK

一个 User Program 不需要自己找空闲 Link、构造所有控制消息、维护 Host 状态或管理 ACK / NAK。

这些工作被统一收进:

1
Network Program

内部。

而 Network Program 又使用三类数据结构:

1
2
3
4
5
6
7
8
HOST Table
→ Remote HOST 对应的 256-bit Link Bitmap

LINK Table
→ Active Link Runtime State

USER Table
→ 一个 User 当前拥有的 Links

其中甚至已经使用:

1
2
3
Bitmap
Hashing
User ID ↔ Network ID Mapping

这些今天仍然十分常见的软件实现技术。

一个 OPENPRIM 在内部会经历:

1
2
3
4
5
6
7
8
9
10
11
12
13
按 HOSTID 查 HOST Table

找第一个 Free Link

创建 LINK Table Entry

记录 User / Buffer / Primary

通过 Link 0 发 Request

等待 Remote ACK

设置 Established Bit

也就是说:

1
API Call

背后已经是一套真正的协议状态机。

但 RFC 9 这套设计最终没有成功。

1971 年的 RFC 100 回顾得非常直接:

1
2
3
4
5
6
7
8
9
10
11
RFC 9、11、22
是一套早期 Host/Host Protocol。

RFC 11 supersedes RFC 9。

但整个协议不够强,
因为它不能在不登录远端 Host 的情况下
完成 inter-host communication。

因此在 1969 年 12 月
这套协议被推翻。

这个失败原因比任何字段都重要。

RFC 9 的设计本质上仍然把网络理解成:

1
2
3
4
5
远程 TTY
+
远程登录
+
登录以后使用程序

它非常适合:

1
Human → Remote Computer

却不够适合:

1
Process → Process

而互联网真正需要的是后者。

随后 RFC 33 会开始重构:

1
2
3
4
5
6
Link
Connection
Process
Socket
NCP
Control Command

把:

1
“登录一台远端机器”

和:

1
“让两个进程建立通信”

逐渐拆开。

所以 RFC 9 最值得今天软件开发者带走的,不是:

1
2
3
256 Links
64 Active
OPENPRIM

这些已经过时的机制。

而是三个更重要的工程经验。

第一:

协议最终必须变成应用可以调用的 API。

第二:

API 背后必须有清晰的状态、资源和数据结构模型。

第三,也是最重要的:

如果底层抽象过度绑定第一代使用场景,下一代需求出现时,整个协议可能不得不重做。

1969 年,第一代使用场景是:

1
人通过 TTY 登录另一台计算机。

几个月以后,大家发现真正需要的是:

1
不同 Host 上的进程直接通信。

于是:

1
Remote Login Model

开始让位于:

1
Interprocess Communication Model

这一步,正是 ARPANET 从“远程终端网络”走向真正通用计算机网络的重要转折。

RFC 9,就是那个旧模型最完整、也最值得研究的一张设计图。


RFC 9 带读:Host Software——从 Logical Link 到 Service Call,早期 Host-Host 协议开始进入操作系统
https://allendericdalexander.github.io/2026/08/17/rfc/rfc00009-host-software/
作者
AtLuoFu
发布于
2026年8月17日
许可协议