跳到主要内容

引言

(这里陈述的既有我个人的意见, 也有我认为代表 Project MAC 网络工作组共识的那些意见。代词 "我" 和 "我们" 用来区分这两者。)

4 月 21 日和 23 日, Thomas P. Skinner 和我与 UCLA 的 Steve Crocker 就网络协议通了电话, 具体谈到我们在 NWG/RFC 46 中提出的建议。讨论的内容如下。(如果我对 Steve 的话转述有误, 希望他能见谅。)

  1. Steve 说他觉得网络参与者日后会认识到动态重连接的必要性。但由于缺乏共识, 它不会纳入最初的实现。(我们 Project MAC 赞成先不把它纳入进去。)

  2. Steve 支持实现 NWG/RFC 46 中所述的 INT 网络命令。

该命令允许一个已同意在某个 socket 连接上接受中断的进程被另一端的进程可靠地中断。中断使进程中止当前的执行, 转而执行它指定为 INT 处理程序的过程。(NCP 并不规定 INT 处理程序。那是更高层协议的职责。)

INT 命令专门用于供第三层的用户控制与通信 (UCC) 协议实现 "退出" 信号。在这种协议下, 请求方和被创建的进程都同意: 针对某个特定 socket 连接、经由 NCP 控制链路传送给被创建进程的 INT 就是标准的 "退出" 信号。被创建的进程提供一个实现该 "退出" 功能的 INT 处理程序。(这并不妨碍其他第三层协议对 INT 作出不同的解释。)

虽然许多系统把 "退出" 实现为 Teletype 输入流中的一个控制字符, 但 CTSS、Multics 等系统把它实现为线路上的 200 ms 间隔。我们 MAC 认为, 前一种做法在网内是不可取的实现 (而后者根本不可能)。我陈述了若干理由 (我认为 Steve 也同意)。

(a) 用于传送退出字符的那条链路可能被阻塞。

(b) 中断虽在 NCP 内部实现最为有效, 但 NCP 不宜对正在传送的数据强加任何特定结构。(见下文的讨论。) 若要让 NCP 扫描数据流寻找控制字符, 就不得不这样做。

(c) 扫描输入流会大大降低 NCP 在那种速度对有效运行至关重要的子系统中的效率。

Steve 指出, 把 INT 实现为 "退出", 不一定就排除 HOST 把输入流中的某个控制字符也解释为 "退出"。

  1. Steve 既反对把实例标记放进 socket 标识符, 也反对在标识符中预留一个空字段留待日后定义。他举出了若干理由:

(a) 同一个用户的多个进程对外来进程应当是无法区分的。(在进程为联合行动而协调一致的某些场合, 我同意这一点。但同一个用户的两个进程都想各自独立地使用网络时, 又该怎么办?)

(b) 想要连接到某个外来用户的进程之一的进程并不知道它想要的那个进程的实例标记, 而且也不容易查出来。

(c) 如果日后发现实例标记确有价值, 加进去会有一些困难。(我认为, 像 socket 标识符长度这样根本的东西, 是极难改动的。)

Tom 说, 也许可以把用户码的低三位留出来, 日后解释为实例标记。他认为单独的字段并不十分重要。

Steve 的论据似乎有道理。也许 Tom 的建议是可行的方向。此事我目前尚未决定。

  1. 我们 (Steve 和我所在的 MAC) 似乎一致认为, 在 NCP 层不应对所传送的数据强加任何特殊结构。对 NCP 而言, 所有要传送的数据都是任意长度的位串。一个令人愉快的结果是, 字符集这个难题不必在这一协议层解决。在 NCP 层纳入字符集规定会拖延对协议达成一致, 并使该字符集更难改动。(如果要有标准字符集, 我们倾向于 ASCII。毕竟, 它是我们的资助机构所倾向的标准。)

我们还同意 Steve 的看法: 在 NCP 协议层不应有可选择的消息回显。(这也是 RFC 44 中 SDC 那些人的立场。)

  1. Shoshani、Long 和 Landsberg 也指出 (RFC 33), 他们倾向于让报文对齐到字边界结束, 而不是采用双重填充。Steve 与我们的看法一致, 也不喜欢双重填充。

  2. 在我们的建议 (RFC 46) 中, 我们主张只为打开的 socket 排队 RFC, 发往不活动或已连接的 socket 的 RFC 则通过 CLS 命令自动拒绝。Steve 建议把这些 socket 的 RFC 短暂排队。如果 RFC 到达后 socket 在一段特定时间内仍处于不可接受的状态, 就拒绝它。这种安排使某些涉及关键竞争的网络命令交互得以实现。这种有限排队的安排在我看来并不算不合理。

  3. Steve、Tom 和我讨论了用户控制与通信 (UCC) 协议的策略。Steve 说他不喜欢我们的 UCC 策略 (RFC 46), 因为它要求维持到请求进程的两条全双工连接并在其间切换。

Steve 提出了另一种建议: 想在某个外来 HOST 上创建用户进程的进程, 向它想创建其进程的那个用户所属的 socket 0 和 1 发出 RFC。如果这些 socket 不活动, NCP 就自动把这些请求转给外来 HOST 的登录器进程。登录器接受连接并执行登录仪式。如果成功, 登录器就创建一个用户进程, 并放开它占用的那些 socket, 以便被创建的进程用它们与请求进程通信。(我注意到, 这里并没有在网络层使用重连接, 因为登录器使用的是最终用户自己的 socket。不过它确实涉及内部重连接。)

Tom 和我反对这一点, 因为它把 UCC 协议引进了 NCP 层。(NCP 必须把所有发往不活动的 socket 0 和 1 的 RFC 转给登录器进程。) 我随口提了一个建议: 也许可以把我们的两个方案合并起来, 让请求方发出一条 "信令" RFC 到 UCC 进程的 "信号" socket。UCC 拒绝该 RFC, 但记住是谁在呼叫。随后它尝试把待创建进程的两个 socket 连接到请求方的 socket, 并通过这些 socket 完成登录仪式。Steve 喜欢这个办法, 并建议我把它写下来。

那次交谈之后, 我想到这个 UCC 策略有若干缺点:

(a) 如果被创建进程的控制 socket 只限于 0 和 1, 就可能出现这种情况: 合法用户无法与外来 UCC 通信, 因为该 UCC 已经在用这些 socket 与冒充者通信。登录器会发现这一点并切断冒充者, 但这是一种令人恼火的安全漏洞。恶意进程可以同时发出多个请求来占住这些 socket, 阻止合法用户接入。更好的办法是允许潜在用户进程的任意 socket 对充当控制通路。这样 UCC 就能同时对互相竞争的请求方进行查询。

(b) Crocker 的方案与合并方案都有同一个缺点: 待登录的用户是靠提供一个属于某个特定用户的 socket 来指定的。登录器现在必须额外检查: 它正在登录的用户确实属于它进行通话所用的那个 socket 对。这似乎与我们所倾向的过程相反: 先识别用户, 再确定其 socket 标识符的用户码。

(c) 用户可能不知道他想在外来 HOST 上登录的那个用户的 socket 用户码。(毕竟, 只要请求方能让外来登录器满意, 请求进程和被创建进程并没有基本理由必须具有相同的用户码。)

(d) 在合并策略中, 请求方无法指定它想要哪个 socket 用户码。UCC 唯一能作的假定是: 请求进程想要登录一个与它自己具有相同 socket 用户码的进程。(这看起来也许并不很重要, 但我构想过这样一种安排: 让一个本地进程存在, 使接在本地 HOST 上的控制台无需在本地登录就能登录到外来 HOST。)

(e) 允许一个进程用自己的 socket 用户码在网内冒充另一个进程 (哪怕是出于最好的用意), 会带来潜在的危险安全漏洞。我认为这应当成为一条基本协议法则: 任何进程都不得在用户码不属于自己的 socket 上请求或接受连接、发送或接收数据。这并不适用于对此类传送负责任的 NCP 进程, 也不妨碍一个特权进程关闭或拒绝外来进程与另一本地进程之间的连接。

我仍然认为我们在 RFC 46 中提出的 UCC 方案是一个可行的好方案。它既不需要 socket 重连接 (无论是贯穿全网地显式进行, 还是在一个 NCP 内部隐式进行), 上面提出的那些反对意见也都不适用。我看到的唯一特别缺点, 是它要求请求进程维持并切换两条全双工连接。我并不认为这是严重的障碍。我倒特别想听听网络参与者对此点的意见。

幸好 UCC 是第三层协议。在我们对 UCC 取得最终一致之前, 第二层 NCP 就可以先行确定下来, 前提是该 NCP 允许实现一个可行的 UCC。

Steve 表示过这样的想法: 不必有最初的标准 UCC, 也可以有若干种 UCC。我们 MAC 不同意。如果我们要彼此交谈, 而不是只在网内有限的 HOST 子集之间交谈, 就必须有一个所有人都实现的初始标准 UCC。(Steve 说还可以实现其他实验性的 UCC, 这当然是对的。)

从理论上说, 每个 HOST 都可以提供多套软件, 让请求进程与实现不同 UCC 的 HOST 上的登录器通信。我认为实际中不会这样运作。每个 HOST 都会实现对它最合意的那个 UCC 协议, 并只提供一套软件, 使请求进程只能与实现同类 UCC 的那些 HOST 通信。

我认为 Project MAC 对于仅仅为了我们自己之间能交谈而实现一个非标准 UCC 并没有多少热情。我们想实现一个在所有站点都得到支持的单一 UCC, 这样我们就能用这个协议登录所有 HOST, 而所有外来 HOST 上的用户也能登录到我们这里。


注: 本 RFC 由 Altair Petrofsky 于 7/97 转成机器可读形式, 以便录入在线 RFC 档案库。