摘要

  • RFC 1073 让客户端一次报告字符窗口的宽与高,并可在本地窗口改变后再次报告;服务器可以同意 NAWS,却不使用数值,也可以在最初同意后停止后续更新。
  • DO/WILL 只建立选项许可,四字节子协商只证明客户端申报过几何状态;服务器终端状态、子进程通知、应用重排与人实际看到的画面,都是后续且独立的事实。

Telnet 基础规范选择了一个克制的起点:网络虚拟终端没有确定的打印宽度和页长。不同机器因此能先以最低共同模型交谈,不必事先掌握对方所有终端特性。可到了 1980 年代后期,Telnet 客户端越来越常运行在图形工作站的可变窗口里。一个会在会话中途改变的矩形,无法再由固定终端的想象来解释。

RFC 1073把选项 31 命名为 NAWS,即“协商窗口尺寸”。它真正修正的不是数字范围,而是权力归属。窗口是客户端的本地对象,客户端完全控制其大小;服务器需要的是一份当前状态报告。报告可以跨网传递,是否采用仍由服务器决定。

同意选项,不等于采用尺寸

服务器可发出 DO NAWS,客户端愿意提供尺寸则回 WILL NAWS;DON'T 与 WON'T 保留拒绝路径。RFC 855给复杂 Telnet 选项规定的思路,是先用这组命令确认双方理解,再进入承载具体数据的子协商。

所以,看到 DO/WILL 只能说明会话进入了某种选项状态。它不能证明客户端后来发送了宽高,更不能证明服务器保存了数值、修改了终端驱动、通知了子进程,或让应用重新排版。把许可当成结果,会在证据链的第一步就越界。

真正的 NAWS 子协商由宽度两个字节、高度两个字节组成,采用 Internet 字节和位序,单位是字符。每个方向最高可到 65,535。Telnet 又把 255 留作 IAC 控制字节,因此四个数据字节里若出现 255,就必须重复一次。接收方只有正确识别边界并去除转义后,才得到那四个逻辑字节。

零也不是“零列”或“零行”。它表示客户端没有提供该方向。服务器可按自己的操作系统规则选择假设,也可能参考另一个选项给出的终端类型。协议保留下来的是“不知道”,不是一个可供直接计算的零尺寸。若监控系统把零写成真实几何值,便把明确的不确定性改造成了假精确。

新报告是一代新状态,不是回执

RFC 的例子先发送 80×24,用户调整窗口后再发送 80×64。第二次不需要重新交换 DO/WILL;本地状态一变,客户端就可以在已建立的选项中发送新值。这是 NAWS 与请求式状态查询的重要差别。

但第二份报告并不确认第一份报告被使用。规范没有定义“子进程已经重排完成”的成功应答。于是同一时刻可能存在多个真实版本:窗口管理器知道新尺寸,Telnet 客户端尚未发送;服务器已经收到,终端状态尚未更新;终端状态已经更新,应用仍按旧值输出。

RFC 1073 直接称尺寸信息为“advisory”。服务器可以接受选项,却不使用发送来的信息。有些服务器操作系统无法在会话开始后更新窗口大小,规范允许它先接受、再发送 DON'T NAWS,禁止后续子协商,同时避免协商循环。客户端窗口仍可能继续变化,只是远端选择不再接收描述。

这不是协议失败,而是控制权分开了:客户端决定窗口事实,服务器决定是否收听以及本地系统能否响应。

NAWS 撤回了服务器并不存在的权力

更早的 NAOL 与 NAOP 分别处理输出行宽和页长。RFC 1073 认为它们不适合图形窗口:两者是双向的,仿佛服务器也能控制客户端的行宽或页长;每个方向又只到 253,而且当时并不常用。

NAWS 把宽高同时发送,因为图形窗口通常同时改变两个轴。更关键的是,它不再把本地窗口称作双方共同讨价还价的对象。服务器可以提出使用 NAWS,却不能借此调整客户端窗口。协议传递的是字符网格,而不是屏幕像素、物理宽度或显示器型号。

因此,RFC 示例中的 300×24 完全可以成立。它说明有 300 个字符列,不是在证明某块玻璃异常宽。若把字符单元换算成像素或设备尺寸,就加入了源数据从未提供的字体、缩放和渲染假设。

类型、速度与几何各有自己的证据边界

RFC 930描述的是终端类型:服务器请求,客户端回答;服务器收到名称并不意味着必须立即改变处理。RFC 1091后来允许客户端轮换多种仿真模式,仍由服务器发起询问。类型是能力与选择语境,不是当前窗口测量。

RFC 1079把选项 32 用于终端速度。双方同意后,请求方再索取一段 ASCII 发送/接收速率,提供方不能自行推送;遇到不支持的速率还可按用途安全取整。NAWS 则允许客户端在窗口改变后自行报告,用二进制的两个轴表达几何。

当 NAWS 某一轴为零时,终端类型或许帮助服务器挑选默认值;它并没有测量那一轴。速度可影响填充与界面策略;它也不是列数。几种选项只有保持分开,接收方才知道每项判断究竟借用了哪一份事实。

IANA Telnet 选项登记表仍把 31 对应 NAWS、32 对应 TERMINAL-SPEED。登记证明编号与文档出处,不证明今天某个产品实现了选项、实现符合规范,或某次会话实际使用过它。

四个字节要经过五个本地主人

RFC 1073 的实现说明给出了一条很有价值的链。在图形环境中,窗口系统先把改变通知 Telnet 客户端;4.3BSD 上,客户端进程可能捕获 SIGWINCH;随后发送 NAWS;服务器可能用 ioctl 更新终端状态,再可能向子进程——通常是 shell——发出窗口变化信号。

窗口管理器、客户端、网络解析器、服务器终端层和应用进程分别掌握一步。每个“可能”都保留了本地决定。NAWS 抵达不能证明 ioctl 成功;终端状态改变不能证明信号送达;信号送达不能证明应用在下一次输出前处理;输出排版正确也不能证明有人看见。

这条链比一个“resize success”字段更适合排障。远端换行异常时,应逐项对照本地变化时间、原始子协商、255 转义、服务器保存值、子进程信号和应用读取的几何代次。

来源与限制

RFC 1073 记录的是 1988 年的提案与一项当时的 Carnegie-Mellon 使用情况,不是普及率调查。RFC 854、855 提供 Telnet 框架;RFC 930、1079、1091 界定邻近属性,却不能证明 NAWS 被采用。IANA 登记也不提供流量或产品事实。

无需把 NAWS 说成 SSH 伪终端、响应式网页或远程桌面的直系祖先,它的历史意义已经足够清楚:协议让一份会变的本地状态能够跨网表达,同时没有把报告提升为命令。窗口由客户端掌握,响应由服务器掌握。