Switch-Router

让TCP容忍丢包

Published at 2022-05-03 | Last Update 2022-05-03

声明: 本文所描述的方案未经验证, 只是单纯的个人想法而已 :)

网络上看到一个问题: 如何提升高丢包率环境的 TCP 传输性能

第一反应是, 既然已经高丢包环境了, 就不要再奢求太多, 毕竟 TCP 所有的”降速”, “重传”等手段都是为了报文能”完整”抵达终点.

除非, TCP 愿意付出一些代价, 比如放宽一些条件: 比如忍受部分报文能被丢弃…

在完美主义者眼中, 可能已经指着鼻子骂了: 允许丢弃, 这还是 TCP 吗 ?!

这确实违背了 TCP 的”可靠传输”的原则。不过,这并不妨碍其具有一定的工程意义.

整体思路是:

  • 发送端应用程序设置报文携带的内容可以被丢失.
  • 接收端分析收到的报文, 当出现丢包时,接收窗口会出现 Gap,如果后续收到的报文指示这个 Gap 的内容是可忍受被丢失,则视为这段 Gap 的报文已经被接收,向发送端反馈推进窗口的 ack。

为了实现这个思路,我们需要发送端报文的 TCP option 携带更多的信息.

这里新设计了一个 TCP 选项, 选项内容包含2个值:

  • lastSeq 表示最近一段可以丢失的报文的起始序号
  • lastLength 表示最近一段可以丢失的报文的长度
  +-----------------------------+-----------------+------------------+-----------------+
  | Kind = last-tolerate-range  |  length = 8     |    lastSeq       |    lastLength   |
  +-----------------------------+-----------------+------------------+-----------------+

举个栗子,发送端发送若干长度为 500 Bytes 或者 1000 Bytes。其中长度为 500 的报文我们认为是重要的 (必须送达),而长度为 1000 的报文则是可以忍受丢失的。

注意 last-tolerate-range指示的范围是可以合并的(长度可能超过 1000),并且不可丢失的报文也会携带该选项。

接收端收到报文后,需要解析 last-tolerate-range 选项。

我们知道当发生丢包时, 接收端会先收到序号更靠后的报文,此时缓冲区形成 gap。接收端此时根据 last-tolerate-range 指示的范围与 gap 的重叠情况,可以进行不同的处理

情形一: 完全覆盖

此时, last-tolerate-range指示的范围完全覆盖了Gap. 此时接收端可以用 0x00 (用其他也可以, 我随便举个例子) 完全填充 Gap,

这段数据被视为已经完全收到,然后向发送端回复 ACK,请求序号3001 之后的报文。

情形二: 部分覆盖

此时, last-tolerate-range指示的范围部分覆盖了Gap。此时接收端同样会将last-tolerate-range指示的范围用 0x00填充,再根据它是否紧跟 Gap 的左边沿回复不同的ACK。两种情况下,应答的序号都为必须送达但还未收到数据的最小序号。

情形三: 完全不覆盖

此时,last-tolerate-range指示的范围完全没有覆盖Gap。此时接收端的处理与普通TCP一致,它会发送ACK请求Gap左边沿序号的数据。

好了,这样一来,我们就把是否可以丢失的权利完全交给应用程序,如果应用程序坚持一个字节都不能丢,那么最终表现的行为就和 TCP 别无二致。反之,如果应用要传输的报文是有区别的,比如对一些音视频直播应用来说,播放流畅更重要,中间的一些数据丢就丢了,这个协议就有用了。