摘要

  • 340 只允许客户端发送文章;正文和终止点全部到达后,240 或 441 才报告这次投稿的结果。
  • 如果连接在最终回复抵达客户端前中断,客户端无法区分“未接收”和“已经接收但确认丢失”。
  • 重试时保持同一 Message-ID,使服务器能把两次尝试识别为同一篇文章,同时不把接收、审核、传播和读者可见性混为一谈。

最危险的沉默发生在发送完成以后

客户端已经没有内容可补发。它送出了标题、正文以及单独一行的句点。注入服务器此时可能已完成检查,把文章交给后续流程,并写出了肯定回复。连接偏偏在回复返程上断开。

客户端面对的是两个外观完全相同的世界:服务器也许以 441 拒绝,或者已经以 240 接收,只是答案没有回来。立即重发可能补救失败,也可能制造重复;放弃重发可能避免重复,也可能永远丢掉唯一一次投稿。

这不是普通的超时。它发生在一方可能已经越过承诺边界、另一方却没有证据的区间。

POST 从一开始就有两道答复

1986 年的 RFC 977 把 POST 写成两个阶段。服务器先用 340 邀请客户端提交文章,或用 440 表明不允许投稿。收到完整文章后,才用 240 报告成功,或用 441 报告失败。

因此,第一次肯定不是“已经接收”,只是“可以发送”。RFC 3977 保留了这条界线,并禁止对 POST 使用流水线:340、440 是对命令的直接答复,240、441 则必须等到多行正文和终止序列之后。

规范希望 240 表示:除非出现无法预见的服务器错误,文章将按需要在本机可用和/或继续传送,可能还要经过处理。如果服务器不想要文章,应以 441 拒绝,而不是先接收再悄悄丢弃。

接收承诺不等于立刻可读

同一段规范又限制了 240 的解释。客户端没有收到肯定答复,就不应假设投稿成功;即使收到,也不能据此认定其他客户端已经能读到文章,仍需例如使用 STAT 明确查询。

RFC 5537 描绘了这些状态为何分离。发布代理准备原型文章,注入代理检查并引入文章;审核者可能接手;中继代理负责传播;服务代理才把文章呈现给读者。一个软件可以兼任多种角色,但每个角色产生的证据仍回答不同问题。

所以,阅读端的“未找到”可能只是审核尚未完成,或某个服务点尚未呈现。它不能倒推出前一条连接是否已经接收文章。把短暂不可见当作失败,会让校验动作本身制造第二篇稿件。

丢失答复时,规范优先保护身份

RFC 3977 直接讨论了这种断线。如果客户端在收到最终答复前失去会话,服务器可能已经发出肯定答复,只是途中丢失。下一条会话中,客户端应先确认文章是否发布,再决定重发;或者确保新尝试仍被分配同一个 Message-ID。

规范偏好后一种办法,理由正是文章可能尚未对读者开放,审核流程就是明确例子。附录进一步建议,邮件或 Netnews 文章每次 POST 都携带内容完全一致的 Message-ID 字段,服务器也应把这些尝试识别为同一篇文章。

这里的 Message-ID 不是交易回执。它不证明第一次已成功,也不验证作者身份。它只是保证第二次尝试仍提出同一个对象。若客户端生成新 ID,即使正文一个字没变,也是在网络中声明另一个可被独立审核、传播和保留的文章身份。

历史记录抑制重复,却不承诺绝对一次

RFC 5536 将 Message-ID 定义为唯一标识,并指出 Netnews 特别依赖唯一性和快速比较。RFC 5537 解释了运行原因:泛洪传播会让服务器从多个邻居处收到同一文章,因此中继和服务代理必须保存已见标识的历史记录,用它拒绝重复。

稳定 ID 让丢失答复后的两次行为可以落到同一历史项上。不过,历史库不能无限增长,站点策略并不相同,故障也可能出现在持久化之前或后续流程之间。因此它不是“恰好一次”的保证。

它改变的是问题结构。没有稳定 ID,运维者面对两篇相似但独立的文章;有了稳定 ID,面对的是同一篇文章的两次尝试,可以分别核对正文接收、最终答复、注入、审核、中继和可读状态。

IANA NNTP 参数注册表 登记了 POST 能力,并把 RFC 3977 列为规范来源。登记只证明名称和语义可互操作,不证明某台服务器允许投稿,也不证明其历史保留多久。

NNTP 留下的设计原则很克制:当工作可能已经提交、确认却不可见时,不要用新身份重做同一件事。协议无法消除未知,但能防止未知自行繁殖。

Sources