摘要

  • Jon Postel 与 Joyce Reynolds 在 1983 年 5 月的 RFC 860 中规定了 TELNET 的第 6 号选项 TIMING-MARK。一端发送 DO TIMING-MARK,另一端若接受,就在数据流的合适位置返回 WILL TIMING-MARK,让请求方找到缓冲数据的分界。
  • 典型场景是第一条命令写错,用户却已提前敲入后面的字符。服务器显示错误后,先丢弃到达的旧输入,等标记返回再继续解释新输入。请求标记的一方负责清理自己的队列。

你敲了一条命令,觉得电脑该懂了,于是又敲下一条。服务器终于看完第一条,发现拼错了,屏幕上冒出一个问号。此时第二条命令可能卡在终端主机的缓冲区,可能在网络上,也可能已经进了服务器的输入队列。服务器若按到达顺序照单全收,一个错字就能把后面一串正常按键带沟里。RFC 860 处理的正是这个细小而恼人的问题:旧输入究竟在哪儿结束?

TELNET 主协议 让两端双向交换数据;选项规范 允许任何一端提出额外能力,由对方答应或拒绝。TIMING-MARK 把这套协商用在一个具体动作上。它的名字里有“时间”,实际发挥作用的是数据流中的位置。两边各有缓冲,用户看到问号和服务器读到后续字符也不是同一个瞬间。

服务器发出 IAC DO TIMING-MARK。对端若愿意配合,就把 IAC WILL TIMING-MARK 放进回程数据流的合适位置。RFC 描述了标记如何先穿过对端的输出缓冲,到达终端侧,再经输入缓冲返回服务器。WILL 到来时,往返路径上就有了一道双方认得的线。服务器可以据此区分用户看见问号之前已经敲下的字,与看见问号后才开始的修正。

文件给出的操作顺序很有画面感。服务器先送出回车、换行、问号,接着请求 Timing Mark 并发出报错信息。它继续读输入,但在收到返回的 WILL 之前把字符扔掉。标记到达后,才重新认真对待键盘。理想的分界点落在用户看到问号之后输入的第一个字符之前。服务器不再靠猜测自己的队列长度来判断人的反应。

垃圾桶有主人。RFC 860 把丢弃无用字符的责任交给发送 DO 的一端。对端负责在自己的数据流里放一个可信的标记;请求方则据此处理自己收到的字符。这个分工还能反过来用:终端一侧若受够了服务器还在喷涌的旧输出,可以请求标记,先停止显示那些内容,等服务器的 WILL 回来再展示新命令之后的输出。谁要整理,谁提出请求,谁动手清队列。标记提供证据,清理是本地动作。

不同答复提供不同程度的把握。WILL 承诺标记放在要求的位置;WON'T 拒绝这种承诺,同时表明先前的数据已被接收;DON'T 通知对方此前的 WILL 被忽略。默认状态下,连接不会主动做这种同步。RFC 也把测量往返延迟列为用途之一,因为明确的折返点有利于观察耗时。但报文里的标记本身记录的是先后顺序。

作者还写了一个急性子的变体:用户预料服务器马上会请求标记,便让自己的终端先在选定位置发出 WILL,然后继续打字。终端软件稍后还得妥善消化姗姗来迟的请求。这一细节很诚实:计算机之间的队列与人眼所见总会错位,协议必须给那个人的“现在”找一个能传回服务器的位置。

一个问号、一趟往返、一个由请求方自己控制的垃圾桶。TIMING-MARK 让服务器在发现错字之后,仍能从用户真正看到错误的地方续上对话。键盘可以抢话;服务器终于知道该从哪一句重新听。