摘要

  • 远端网关故障主要由路由系统绕行;真正必须由主机亲自识别的,是已经无法回送 ICMP 建议的首跳网关。它一旦死亡,数据包只会消失。
  • 路由收敛期间的一条 ICMP 报错不能独自终止连接。重传和用户超时说明问题已经严重,报错则提供可能原因,两者合起来才形成可用证据。
  • TCP 确认只覆盖传输序列,不等于应用完成。早期 SMTP 接收崩溃与空闲 Telnet 会话证明,应用仍要定义自己的最终回执、等待时间和存活判断。

TCP 已收完,邮件仍未完成

RFC 816 讨论故障时,没有把“包到了”当成故事终点。早期一些邮件接收程序会在收完全部正文之后、返回 SMTP 层确认之前崩溃。由于所有文本已经得到 TCP 确认,发送方没有待确认数据,传输层的重传计时器也就看不见这个故障。邮件发送程序只能等一个永远不会出现的应用回复。

问题不在 TCP 漏做了什么。TCP 能证明的是对端传输层已接收相应序列空间,不能证明接收进程仍然活着、邮件已落盘,或这次操作已经在业务上完成。只有 SMTP 知道哪一条回复才算邮件层的结果。

给 SMTP 增加计时器也不是随手填一个秒数。时间太短,大文件发往慢主机时会把正常进度误判成失败;时间太长,真正的崩溃又会拖很久才暴露。当时一些邮件程序把等待时间与消息大小联系起来。这个做法未必是今天的通用答案,但它指出了正确的决策位置:知道操作语义和负载形态的应用,才有资格规定最终等待边界。

死去的首跳无法解释自己的死亡

在 IP 层,RFC 816 把主机责任缩得很窄。网关彼此交换对邻接网络和网关状态的最新看法。某处故障后,它们会短暂混乱,随后重建拓扑。若坏掉的是远处一跳,邻近故障点的网关应完成隔离和绕行,主机无需维护整个互联网地图。

首跳网关却是特殊情况。主机持续把数据交给一台已经崩溃的直接网关时,这台机器无法返回 ICMP Redirect,也无法返回 Destination Unreachable。其他网关即使早已收敛到完全正确的路径,也接不到这些数据。包会在主机眼前消失,没有任何错误从故障设备返回。

因此,主机需要识别自己正在使用的首跳已经失效,并换用另一台直接可达网关。这是一项局部职责,不是让每台主机复制路由系统。可观察范围决定责任范围:远端故障交给最接近它的路由节点,沉默首跳交给直接把包交给它的主机。

这种安排也限制了结论。发现首跳死亡,只能证明当前交付入口需要替换;它不证明目的主机、远端服务或业务对象已经死亡。

收敛中的报错带着时间

RFC 816 把 Redirect 与 Destination Unreachable 都称作建议。Redirect 告诉主机还有更好的直接网关,并说明触发它的那个数据报已经被转发。Unreachable 表示在报错者当时掌握的状态下,目标暂不可达。

故障刚发生时,各网关掌握的信息并不同时更新。一条数据报可能恰好穿过路由重新协商的窗口,收到一条孤立的 Unreachable;几秒或几分钟后,新路径却已经建立。如果 TCP 因这一条消息立即终止连接,端点就失去了让互联网内部自我修复的机会。

这并不等于忽略 ICMP。报错在新建连接时可能强烈提示地址错误,在连接超时之后可以说明最可能的原因,Parameter Problem 还可能指出实现问题。它的权重取决于类型、代码、发送者、连接阶段、到达时间以及其他证据是否一致。

一条报错描述的是某个网络层节点在某一时刻看见的事实。它可以向上传递,却不因此获得替 TCP 或应用结束工作的权力。把建议留下,比把它夸大成绝对真相更有用。

不必等到诊断完美才开始恢复

主机怎样发现死网关?RFC 816 比较了几种方案。某些底层网络能直接报告目标主机死亡。主机也可以不断用 ICMP Echo 轮询网关,但为了及时发现问题,轮询频率会给主机、链路和网关造成难以接受的负担。除非专门分析证明开销可承受,文件明确禁止这种持续探测。

触发式轮询只在上层出现异常时启动。例如 TCP 对同一段多次重传后,向 IP 提供负面提示,再由 IP 探测首跳。开销降低了,代价是确认故障可能来得太晚。

触发式重选则把不确定性变成可逆试验。上层抱怨服务异常时,IP 先从已知列表中换一台网关。如果原网关确实死亡,新路立即缩短恢复时间;如果判断错了,活着的替代网关仍可转发数据,并通过 Redirect 把主机指回更优选择。

这里的关键不是盲目切换,而是错误动作有明确边界和修正路径。主机可以在没有全局确定性的情况下做局部动作,却不能把一次试探固化成永久路由权威。

RFC 1122 后来把要求写得更明确:IP 必须发现路由缓存中的下一跳故障并选择替代网关,同时承认仍没有一个普遍令人满意的完整算法。持续 ping 首跳依然被禁止;来自 TCP、链路、ARP、确认报文或 Redirect 的正负建议,虽然让层间接口更复杂,却是首选方向。

超时说明严重,报错解释原因

TCP 面对待确认数据时会重传,直到收到 ACK 或达到等待边界。多次重传可以向 IP 表明路径需要检查;IP 和底层网络则把 ICMP 或链路错误送回 TCP。两股信息流方向相反,回答的问题也不同。

重传说明预期进度没有发生。用户超时表示调用 TCP 的一方已经不再愿意等。ICMP 则提供失败原因的线索。RFC 816 主张把这些消息以一致方式送到上层,因为只有上层知道这条连接是人在使用,还是程序正在执行一项可重试操作。

RFC 1122 随后规定,若已建立连接收到规定范围内的软性 Destination Unreachable,TCP 不得因此直接中止,并应让应用获得这项信息。RFC 5461 解释了这份耐心的收益与代价:临时故障可能在路由重建后消失,但永久不可达的第一个地址也可能拖慢对其他地址的尝试。它记录过连接建立阶段的非标准捷径,同时明确没有改变标准行为。

今天的 RFC 9293 仍把重传超时与用户超时分开。前者重发队列前端的数据段,后者清空队列、报告连接被中止、删除连接状态并关闭。两个同名“超时”其实服务于不同权限:一个继续恢复,一个终止等待。

没有待发数据,也就没有传输报警

RFC 816 的 Telnet 例子展示了另一面。用户思考时会话没有流量,此时路径可能已经断开。用户下一次敲键会发现连接死亡;服务器一端若没有数据要发,就不会产生待确认段,也不会触发同样的 TCP 重传计时。

应用可以偶尔发起询问,但给大系统的每个空闲用户高频探测,又会复制持续轮询的成本。更合理的办法是由应用依据空闲时间、会话价值和恢复代价,在确有怀疑时检查。

邮件例子是“传输成功而应用未完成”,Telnet 例子是“路径已坏而传输层没有待办工作可失败”。两者都说明,可靠性不是把所有责任推给更低一层。低层只能判断自己掌握的状态,应用必须为自己承诺的结果提供证据。

分层不是推责,而是避免越权

RFC 816 最耐久的部分,不是哪一个轮询间隔或某种网关列表,而是证据与决策的对应关系。首跳沉默允许本地换路;孤立 ICMP 提供带时间的网络意见;重传与用户超时定义传输进度和耐心;应用回执证明应用目标。

这些事实应该相互补充,而不是互相覆盖。网络报错不必被当成谎言,也不能被升级为业务失败。TCP ACK 不是应用收据,应用超时也不能倒推出网络一定断了。

报错之所以有用,正因为它只是建议。有限权力让互联网能在暂时矛盾中继续恢复,也迫使真正承诺结果的那一层亲自给出最后证据。

来源