摘要

  • DELE只在受独占锁保护的TRANSACTION状态中为邮件加上删除标记;RSET可以在正常退出前撤回全部标记。
  • 只有客户端从TRANSACTION发送QUIT才进入UPDATE。异常断线不得删除邮件,但UPDATE本身并非原子操作:发生故障时可能只删除部分标记邮件。

断掉的电话线不能替用户作决定

一台拨号上网的电脑下载三封邮件,并要求服务器删除原件。服务器逐一答复成功,却在最后一封写入本地磁盘之前掉线。如果每次DELE都立即生效,同一次故障既阻止本地保存,又毁掉远端副本。

POP3选择把代价放在更容易修复的一边。DELE使邮件在本次会话里表现得像已删除,后续命令不能再引用它的编号,但真实数据仍在maildrop中。客户端可以发送RSET清除全部标记。只有正常的QUIT把会话送入UPDATE,服务器才开始执行物理删除。

服务器知道自己发出了字节,却不知道客户端是否已经可靠落盘。协议无法检查另一端的磁盘,只能选择双方都能观察到的信号。明确告别意味着“现在可以尝试兑现这些意图”;TCP连接突然消失则不具备这种权限。

先确认接收、再放弃副本的思想早于POP3

1984年的RFC 918已经区分只读的RETR和带删除意图的RDEL。客户端收完数据后还要发送RCVD,服务器才尝试删除;RSET可以中止事务,并要求正确关闭和解锁邮箱。

1985年的POP2规范RFC 937使用ACKD确认接收并给当前邮件加删除标记,真正改变邮箱要等到会话结束或切换邮箱、释放邮箱之时。

早期命令后来被替换,问题却没有消失。在不稳定网络上,“服务器已经发出”与“客户端已经可靠拥有”不是同一事件。POP协议的演进,就是逐渐把两者之间的空隙写成公共规则。

三个状态把延迟删除变成了协议结构

1988年的RFC 1081首次定义POP3的AUTHORIZATION、TRANSACTION和UPDATE。认证成功后,服务器打开用户的maildrop并取得独占锁;客户端在TRANSACTION中列举、读取和标记邮件;QUIT触发UPDATE,服务器尝试删除标记邮件、释放锁并关闭连接。

三个状态隔开三种权力:AUTHORIZATION决定谁能进入,TRANSACTION提供稳定而可撤销的操作视图,UPDATE承担不可逆效果。因此在尚未认证时发送QUIT只会结束连接,不会进入UPDATE,因为根本没有一个已认证的邮箱事务需要兑现。

邮件编号也只属于这次工作视图。DELE之后,协议拒绝再次引用该编号,即使底层邮件仍未移除。会话的逻辑状态可以先前进,存储状态则等到边界之后再改变。

RSET把反悔设计成正常路径

RSET不是补丁式的紧急命令,而是最小POP3实现必须支持的命令。它一次撤回所有删除标记。客户端可能发现磁盘不足、某封邮件解析失败,或在退出前改变“保留服务器副本”的策略;协议允许它收回尚未生效的意图。

独占锁保护这段反悔窗口。RFC 1725明确说明,必要时锁要防止邮件在进入UPDATE前被修改或移走。否则另一客户端可能在本次会话还依赖旧编号时重排同一邮箱。

这并不是完整的数据库事务。POP3没有分布式共识、UPDATE后的撤销,也没有规定统一日志和崩溃恢复。它只把一个很窄的可逆区间定义清楚。

异常结束必须保留所有邮件

早期POP3比较清楚地描述正常流程,却没有同样清楚地描述断线。RFC 1725把“澄清连接中断行为”列为修订目的,并规定:凡不是客户端主动发送QUIT造成的终止,都不得进入UPDATE,也不得删除maildrop中的任何邮件。闲置超时同样只关闭TCP连接。

1996年成为STD 53的RFC 1939保留了这条规则,还说明原因:异常终止时,客户端可能尚未成功接收或保存邮件。

协议没有假装能证明客户端磁盘的真实状态。断线前也许已经收全,正常QUIT也不等于完成了备份。它只是选择保守的共同信号。下次重复下载可以清理;唯一副本消失往往无法补救。

QUIT是一道提交门,而不是原子性魔法

把QUIT类比为commit只能解释“越过边界”,不能带来数据库的全部保证。RFC 1939规定,服务器在UPDATE中尝试移除所有标记邮件;若资源不足或发生其他错误,可能删除其中一些、全部或一个也没有。绝对不能删除未标记邮件。无论成功与否,服务器随后释放锁并关闭连接。

因此,边界之前的保证很强:标记可以撤销,异常终止保留全部邮件。边界之后的结果却可能部分成功。如果最后答复也丢失,客户端甚至无法确定哪些已经删除。

规范选择承认这种不确定性,因为简单服务器和多样存储后端未必能实现全有或全无。DELE后的+OK只能解释为“标记已接受”,不能显示成“远端副本已经消失”。

后来的保留策略仍服从UPDATE

当把邮件留在服务器上逐渐普遍,RFC 2449用EXPIRE能力声明保留政策。EXPIRE NEVER表示政策不删除;EXPIRE 0允许服务器把成功读取的邮件视为隐式加标记,但生效点仍是会话进入UPDATE之时。

扩展可以改变哪些邮件被标记,却没有把一次读取直接变成破坏,也没有让异常断线冒充告别。小型状态机因此能承接新政策而不牺牲原有安全边界。

告别保护的是保管权,不是礼貌

客户端决定何时完成预期的读取序列,服务器决定存储删除是否成功,网络只能打断它们,不能替代客户端授予不可逆权限。POP3让不确定性的后果更偏向重复邮件和后续核对,而不是静默丢失。

先标记,允许重置,明确跨过边界,尝试删除,诚实报告部分失败——这就是机制的全部力量。删除直到告别才开始成真;即使告别之后,协议也没有谎称世界一定整齐地一次改变。

来源与边界

历史源头见RFC 918和RFC 937;首个POP3状态机见RFC 1081,其延续见RFC 1225与RFC 1460。RFC 1725明确断线规则,RFC 1939规定现行行为、部分失败和保管理由,RFC 2449补充EXPIRE。它们不证明当前部署比例、任何产品的存储实现,也不承诺原子或恰好一次删除。