跳转至

可靠传输

动机:不可靠信道上建立可用抽象

底层可能损坏、丢失、重复或重排分组。可靠传输希望向应用呈现更简单的结果:数据不损坏、不重复,并按预期顺序交付。这个抽象不是由单一字段实现,而是多种机制组成的闭环。

从最小协议逐步演进

第一步:检测损坏

发送方附加校验值,接收方重新计算。若不一致,接收方知道数据不可直接交付。但“知道坏了”不等于“知道正确内容”,所以还需要反馈与重传。

第二步:确认与重传

接收方返回 ACK 表示已正确接收,发送方在得到 ACK 前保留数据。若收到否定确认或等待超时,发送方重传。

新问题随之出现:数据已经到达,但 ACK 丢了。发送方会重传相同数据,接收方必须识别重复。

第三步:序号与去重

停止等待协议一次只允许一个未确认分组,因此 1 比特序号 0、1 交替就能区分“新分组”和“上次重传”。接收方对重复分组再次确认,但不重复交付。

这个最小例子包含可靠传输的核心思想:校验判断可用性,序号标识身份,ACK 传回接收状态,定时器处理无响应,重传修复丢失。

停止等待为何浪费带宽

若发送一个分组只需 0.1 ms,但往返时延 RTT 为 100 ms,发送方绝大多数时间都在等待确认。粗略利用率为:

\[ U\approx \frac{d_{trans}}{RTT+d_{trans}}, \]

代入后约为千分之一。提高链路带宽只会让发送更快,等待传播的比例反而更高。

滑动窗口与流水线

滑动窗口允许多个分组同时在途。发送方窗口可分为已确认、已发送未确认、允许发送但尚未发送以及暂不可发送区域。ACK 到达后窗口向前滑动,新的序号进入可发送范围。

为了让高带宽长时延链路保持忙碌,在途数据量至少应接近带宽时延积:

\[ BDP=R\times RTT. \]

例如 100 Mb/s、RTT 100 ms 的路径,带宽时延积约 10 Mb,即 1.25 MB。若窗口只有 64 KB,发送方即使没有丢包也无法跑满链路。

累积确认与选择确认

TCP 的确认号通常表示“下一个期望字节”,因此具有累积含义。若接收方确认 5001,表示此前连续字节均已收到。乱序到达的后续数据可暂存在接收缓存,但累积确认可能停在缺口处。

选择确认 SACK 可额外告诉发送方哪些不连续区间已经到达,使发送方只重传真正缺失的部分。它提高多丢包场景效率,代价是双方维护更复杂的区间状态。

定时器如何选择

重传超时太短,会把只是排队较久的数据误判为丢失,制造多余重传;太长,则真正丢失后恢复缓慢。实际协议根据测量到的 RTT 平滑估计超时时间,并为波动留余量。

仅靠超时恢复仍慢。TCP 可从重复确认或 SACK 缺口推断某段可能丢失,提前快速重传。要注意丢包信号与拥塞控制相连:重传修复可靠性,降低发送速率则试图缓解造成丢包的拥塞。

TCP 以字节编号

TCP 序号标识字节而不是“第几个包”。若一个段从序号 1000 开始,携带 500 字节,那么下一个连续字节序号是 1500。底层分段大小可能因 MTU 和实现改变,但应用看到的仍是连续字节流。

SYN 和 FIN 也占用序号空间,便于可靠确认连接的开始与结束。序号是有限宽度字段,会回绕;协议依靠窗口和报文生存时间避免把很久以前的旧数据误认成当前数据。

可靠的边界

传输层可靠不等于业务可靠。客户端发送“转账 100 元”后连接断开,可能是请求没到,也可能是服务器已执行但响应丢失。TCP 重连后无法替应用判断。业务层需要事务标识、幂等键、查询接口或去重表。

同样,TCP 成功写入本机发送缓冲区不等于对端应用已经持久化数据。每层确认的只是自己的责任范围,不能把下层确认夸大为业务承诺。

自测

为什么窗口扩大能提高长距离链路吞吐,却不能无限扩大?

足够大的窗口可填满带宽时延积;继续扩大可能让队列累积、延迟上升和突发丢包,还受接收缓存、序号空间与拥塞控制约束。