摘要

  • 客户端把 RRQ 或 WRQ 发给服务器的已知端口 69;服务器接受后另选一个 TID,与客户端 TID 一同成为本次传输余下报文的 UDP 端口边界。
  • 错误源 TID 会收到错误码 5,却不应打断已接受的会话;后来的重传修正、块大小和窗口扩展都没有把这个短期边界升级成身份认证。

69 端口只负责接单

TFTP 的“简单”不是没有状态。RFC 783 为内存和功能都有限的环境规定了一种读写文件办法:没有目录浏览,没有完整的 FTP 控制语言,也没有用户认证。它把必要状态压缩到少数报文类型、块编号、计时器和传输标识符中。

客户端首先选择自己的 TID,并把 RRQ 或 WRQ 送到服务器的已知 TID 69。读取获得许可时,服务器从自己选择的 TID 发出 DATA 1;写入获得许可时,它从该 TID 发出 ACK 0。两个 TID 被交给 UDP 作为源、目的端口,之后的报文都沿用这一对数值。

因此,69 表示“这里可以提出新请求”,不表示“所有数据都必须从这里回来”。服务器可以继续在 69 上接收新请求,同时用不同 TID 隔离并发传输。只按静态服务端口观察 TFTP,会看见一次合法的端点迁移,却把它误报成异常。

第一份正面答复确定边界

RFC 1350 要求 TID 尽量随机选择,降低短时间内重复使用同一数值的机会。客户端收到第一份正面答复后,服务端 TID 就确定下来。其后的源 TID 必须匹配。

这条规则解决的不只是恶意输入,也包括网络自身制造的重复。若最初的请求被复制,服务器可能为两份请求分别创建传输,并从两个不同 TID 回复。客户端接受先到的那一份;后到的答复属于另一段对话。它应被丢弃,发送错误码 5“Unknown transfer ID”,而已经选中的传输继续运行。

这正是 RFC 1350 特别指出的非终止错误。多数 ERROR 报文意味着交换结束,而且 ERROR 本身既不确认也不重传。错误 TID 不同:它的意义是“你不属于这次交换”,如果顺手终止正确交换,边界便失去了保护作用。

但 TID 的证明力必须止于这里。随机端口不是密码学身份,不验证服务器,也不保证文件来源、内容或权限。它只说明某个数据报是否落在双方已经接受的短期二元组内。

一个块就是全部未决状态

基础 TFTP 采用停等方式。DATA 从块 1 开始,每一块在下一块发送前都要收到对应 ACK。固定 512 字节的块让发送方只需保留当前块,接收方无需为乱序重组保存窗口。短于 512 字节的 DATA 表示结束;文件长度正好是 512 的整数倍时,还要用一块零长度 DATA 明确终点。

最后一个 ACK 也可能丢失。接收方发送它后可以短暂等待;若终止 DATA 再次到来,就再次确认。完成不是某个瞬间的主观判断,而是双方对最后一块的可重复闭合。

这种设计把进度证据放在很小的组合里:预期 TID、块编号和计时器。块号前进代表新状态;旧块或旧 ACK 代表重复;错误 TID 属于别的交换;超时才可能授权重发。

旧确认曾经复制出拥塞

早期规则允许任何一方在收到旧的重复数据报后重发当前报文。一次延迟与超时就可能触发连锁反应:旧 DATA 到达,接收方再发 ACK;旧 ACK 到达,发送方又发 DATA;此后每一块和每一个确认都出现两份。

RFC 1123 把它称为“魔法学徒综合征”。文件内容未必错误,传输仍可能失败。重复流量加重最初造成延迟的拥塞,制造更多超时,最终让一个本应简单的交换被自己的恢复机制拖垮。

修正明确收回了旧 ACK 的触发权:DATA 发送方不能仅因收到重复 ACK 就重发当前块。重传应由相关超时或状态规则授权,而不是由任何陈旧信号授权。RFC 1350 取代 RFC 783 时纳入了修正。

扩大吞吐仍要先达成一致

停等对小型实现友好,对高时延或大文件效率不高。RFC 2347 把选项附在初始 RRQ 或 WRQ 中,但只有客户端能发起协商。服务器只能在 OACK 中确认客户端确实请求过的选项;不支持的选项可以省略,未获确认的选项视为从未提出。

读取时,客户端用 ACK 0 接受 OACK;写入时,用 DATA 1 接受。RFC 2348 允许协商块大小,RFC 2349 允许协商重传间隔并预告传输大小。RFC 7440 又允许连续发送一个窗口的 DATA,再用窗口末块的 ACK 确认;窗口为 1 时仍是原来的停等。

这些扩展改变了“确认前允许多少工作”,没有改变“哪两个端点属于本次传输”。更大的块和窗口也没有增加认证、保密、访问控制或文件真伪证明。容量增加,责任反而更需要显式保存。

协议之外才是授权

实际部署还要回答 TFTP 无法回答的问题:哪些地址可以请求,服务器公开哪一段文件树,是否允许写入,启动镜像由谁签署,NAT 或防火墙如何只为已接受的 TID 建立有限状态,日志如何把 69 端口上的请求与后续动态端口关联。

如果中间设备只放行 69,合法传输会在第一块失败;如果为了兼容而放开所有 UDP 端口,又会抹去本应精确的边界。正确做法是观察初始请求和第一份正面答复之间的状态转换,而不是选择“全封”或“全开”。

TFTP 的历史因此展示了一种很小却耐用的分权:公共端口拥有接收请求的职责,双方拥有选择临时端点的职责,块编号拥有证明进度的职责,超时拥有触发恢复的职责。任何一个字段都不应越权替代文件授权和来源信任。

来源与边界

基础历史来自 RFC 783、RFC 1123 与 RFC 1350。选项机制见 RFC 2347、RFC 2348、RFC 2349 和 RFC 7440;IANA 登记表记录 69 端口。这些资料不统计 2026 年部署,也不证明 TID 能认证端点、授权文件或校验内容。