TCP 协议快被淘汰了,UDP 协议才是新世代的将来?

TCP 协议能够说是今天互联网的基石,做为可靠的传输协议,在今天几乎全部的数据都会经过 TCP 协议传输,然而 TCP 在设计之初没有考虑到现今复杂的网络环境,当你在地铁上或者火车上被断断续续的网络折磨时,你可能都不知道这一切可能都是 TCP 协议形成的。本文会分析 TCP 协议为何在弱网环境下有严重的性能问题[^1]。算法

底层的数据传输协议在设计时必需要对带宽的利用率和通讯延迟进行权衡和取舍,因此想要解决实际生产中的所有问题是不可能的,TCP 选择了充分利用带宽,为流量而设计,指望在尽量短的时间内传输更多的数据[^2]。服务器

在网络通讯中,从发送方发出数据开始到收到来自接收方的确认的时间被叫作往返时延(Round-Trip Time,RTT)。网络

弱网环境是丢包率较高的特殊场景,TCP 在相似场景中的表现不好,当 RTT 为 30ms 时,一旦丢包率达到了 2%,TCP 的吞吐量就会降低 89.9%[^3],从下面的表中咱们能够看出丢包对 TCP 的吞吐量极其显著的影响:

tcp

RTT TCP 吞吐量 TCP 吞吐量(2% 丢包率)
0 ms 93.5 Mbps 3.72 Mbps
30 ms 16.2 Mbps 1.63 Mbps
60 ms 8.7 Mbps 1.33 Mbps
90 ms 5.32 Mbps 0.85 Mbps


本文将分析在弱网环境下(丢包率高)影响 TCP 性能的三个缘由:性能

  • TCP 的拥塞控制算法会在丢包时主动下降吞吐量;学习

  • TCP 的三次握手增长了数据传输的延迟和额外开销;大数据

  • TCP 的累计应答机制致使了数据段的传输;优化

在上述的三个缘由中,拥塞控制算法是致使 TCP 在弱网环境下有着较差表现的首要缘由,三次握手和累计应答二者的影响依次递减,可是也加重了 TCP 的性能问题。spa

拥塞控制

TCP 拥塞控制算法是互联网上主要的拥塞控制措施,它使用一套基于线増积减(Additive increase/multiplicative decrease,AIMD)的网络拥塞控制方法来控制拥塞[^4],也是形成 TCP 性能问题的主要缘由。操作系统

第一次发现的互联网拥塞崩溃是在 1986 年,NSFnet 阶段一的骨干网的处理能力从 32,000bit/s 降到了 40bit/s,该骨干网的处理能力直到 1987 和 1988 年,TCP 协议实现了拥塞控制以后才获得解决[^5]。正是由于发生过网络阻塞形成的崩溃,因此 TCP 的拥塞控制算法就认为只要发生了丢包当前网络就发生了拥堵,从这一假设出发,TCP 就使用了慢启动和线增积减[^6]的机制实现拥塞控制。

tcp-congestion-control

图 1 - TCP 的拥塞控制机制

每个 TCP 链接都会维护一个拥塞控制窗口(Congestion Window),拥塞控制窗口的做用有两个:

  1. 防止发送方向接收方发送了太多数据,致使接收方没法处理;

  2. 防止 TCP 链接的任意一方向网络中发送大量数据,致使网络拥塞崩溃;

除了拥塞窗口大小(cwnd)以外,TCP 链接的双方都有接收窗口大小(rwnd),在 TCP 链接创建之初,发送方和接收方都不清楚对方的接收窗口大小,因此通讯双方须要一套动态的估算机制改变数据传输的速度,在 TCP 三次握手期间,通讯双方会经过 ACK 消息通知对方本身的接收窗口大小,接收窗口大小通常是带宽延迟乘积(Bandwidth-delay product, BDP)决定的[^7],不过在这里咱们就不展开介绍了。

客户端可以同时传输的最大数据段的数量是接收窗口大小和拥塞窗口大小的最小值,即 min(rwnd, cwnd)。TCP 链接的初始拥塞窗口大小是一个比较小的值,在 Linux 中是由 TCP_INIT_CWND 定义的[^8]:

  1.  
    /* TCP initial congestion window as per rfc6928 */
  2.  
    #define TCP_INIT_CWND 10

初始拥塞控制窗口的大小从出现以后被屡次修改,几个名为 Increasing TCP's Initial Window 的 RFC 文档:RFC2414[^9]、RFC3390[^10] 和 RFC6928[^11] 分别增长了 initcwnd 的值以适应不断提升的网络传输速度和带宽。

tcp-congestion-window

图 2 - TCP 拥塞控制窗口的线増积减

如上图所示,TCP 链接发送方的拥塞控制窗口大小会根据接收方的响应而变化:

  1. 线性增加:当发送方收到了接收方的 ACK 时,拥塞窗口大小会加一;

  2. 积式减小:当发送方发送的数据包丢包时,拥塞控制窗口会减半;

若是 TCP 链接刚刚创建,因为 Linux 系统的默认设置,客户端可以同时发送 10 个数据段,假设咱们网络的带宽是 10M,RTT 是 40ms,每一个数据段的大小是 1460 字节,那么使用 BDP 计算的通讯双方窗口大小上限应该是 35,这样才能充分利用网络的带宽:

然而拥塞控制窗口的大小从 10 涨到 35 须要 2RTT 的时间,具体的过程以下:

  1. 发送方向接收方发送 initcwnd = 10 个数据段(消耗 0.5RTT);

  2. 接收方接收到 10 个数据段后向发送方发送 ACK(消耗 0.5RTT);

  3. 发送方接收到发送方的 ACK,拥塞控制窗口大小因为 10 个数据段的成功发送 +10,当前拥塞控制窗口大小达到 20;

  4. 发送方向接收方发送 20 个数据段(消耗 0.5RTT);

  5. 接收方接收到 20 个数据段后向发送方发送 ACK(消耗 0.5RTT);

  6. 发送方接收到发送方的 ACK,拥塞控制窗口大小因为 20 个数据段的成功发送 +20,当前拥塞控制窗口大小达到 40;

从 TCP 三次握手创建链接到拥塞控制窗口大小达到假定网络情况的最大值 35 须要 3.5RTT 的时间,即 140ms,这是一个比较长的时间了。

早期互联网的大多数计算设备都经过有线网络链接,出现网络不稳定的可能性也比较低,因此 TCP 协议的设计者认为丢包意味着网络出现拥塞,一旦发生丢包,客户端疯狂重试就可能致使互联网的拥塞崩溃,因此发明了拥塞控制算法来解决该问题。

可是现在的网络环境更加复杂,无线网络的引入致使部分场景下的网络不稳定成了常态,因此丢包并不必定意味着网络拥堵,若是使用更加激进的策略传输数据,在一些场景下会获得更好的效果。

三次握手

TCP 使用三次握手创建链接应该是全世界全部工程师都十分了解的知识点,三次握手的主要目的是避免历史错误链接的创建并让通讯的双方肯定初始序列号[^12],然而三次握手的成本至关高,在不丢包的状况下,它须要创建 TCP 链接的双方进行三次通讯。

basic-3-way-handshake

图 3 - 常见的 TCP 三次握手

若是咱们要从北京访问上海的服务器,因为北京到上海的直线距离约为 1000 多千米,而光速是目前通讯速度的极限,因此 RTT 必定会大于 6.7ms:

然而由于光在光纤中不是直线传播的,真正的传输速度会比光速慢 ~31%[^13],并且数据须要在各类网络设备之间来回跳转,因此很难达到理论的极限值。在生产环境中从北京到上海的 RTT 大概在 40ms 左右,因此 TCP 创建链接所须要最短期也须要 60ms(1.5RTT)。

在网络环境较差的地铁、车站等场景中,由于丢包率较高,客户端很难与服务端快速完成三次通讯并创建 TCP 链接。当客户端长时间没有收到服务端的响应时,只能不断发起重试,随着请求次数逐渐增长,访问的延迟也会愈来愈高。

因为大多数的 HTTP 请求都不会携带大量的数据,未被压缩的请求和响应头大小在 ~200B 到 2KB 左右,而 TCP 三次握手带来的额外开销是 222 字节,其中以太网数据帧占 3 * 14 = 42 字节,IP 数据帧占 3 * 20 = 60 字节,TCP 数据帧占 120 字节:

tcp-three-way-handshake-overhead

图 4 - TCP 三次握手的额外开销

虽然 TCP 不会为每个发出的数据段创建链接,可是三次握手创建链接须要的成本仍是至关高,不只须要额外增长 1.5RTT 的网络延时,还须要增长 222 字节的额外开销,因此在弱网环境下,经过三次握手创建链接会加重 TCP 的性能问题。

重传机制

TCP 传输的可靠性是经过序列号和接收方的 ACK 来保证的,当 TCP 传输一个数据段时,它会将该数据段的副本放到重传队列上并开启计时器[^14]:

  • 若是发送方收到了该数据段对应的 ACK 响应,当前数据段就会从重传队列中删除;

  • 若是发送方在计时器到期之间都没有收到该数据段对应的 ACK,就会从新发送当前数据段;

TCP 的 ACK 机制可能会致使发送方从新传输接收方已经收到了数据段。TCP 中的 ACK 消息表示该消息以前的所有消息都已经被成功接收和处理,例如:

  1. 发送方向接收方发送了序号为 1-10 的消息;

  2. 接收方向发送方发送 ACK 8 响应;

  3. 发送方认为序号为 1-8 的消息已经被成功接收;

这种 ACK 的方式在实现上比较简单,更容易保证消息的顺序性,可是在如下状况可能会致使发送方重传已经接收的数据:

tcp-retransmission-al

图 5 - TCP 的重传策略

如上图所示,接收方已经收到了序号为 2-5 的数据,可是因为 TCP ACK 的语义是当前数据段前的所有数据段都已经被接收和处理,因此接收方没法发送 ACK 消息,因为发送方没有收到 ACK,全部数据段对应的计时器就会超时并从新传输数据。在丢包较为严重的网络下,这种重传机制会形成大量的带宽浪费。

总结

TCP 协议的一些设计在今天来看虽然仍然具备巨大的价值,可是并不能适用于全部场景。为了解决 TCP 的性能问题,目前业界有两种解决方案:

  1. 使用 UDP 构建性能更加优异、更灵活的传输协议,例如:QUIC[^15] 等;

  2. 经过不一样的手段优化 TCP 协议的性能,例如:选择性 ACK(Selective ACK, SACK)[^16],TCP 快开启(TCP Fast Open, TFO)[^17];

因为 TCP 协议在操做系统内核中,不利于协议的更新,因此第一种方案目前发展的更好,HTTP/3 就使用了 QUIC 做为传输协议[^18]。咱们在这里从新回顾一下致使 TCP 性能问题的三个重要缘由:

  • TCP 的拥塞控制在发生丢包时会进行退让,减小可以发送的数据段数量,可是丢包并不必定意味着网络拥塞,更多的多是网络情况较差;

  • TCP 的三次握手带来了额外开销,这些开销不仅包括须要传输更多的数据,还增长了首次传输数据的网络延迟;

  • TCP 的重传机制在数据包丢失时可能会从新传输已经成功接收的数据段,形成带宽的浪费;

TCP 协议做为互联网数据传输的基石能够说是当之无愧,虽然它确实在应对特殊场景时有些问题,可是它的设计思想有着很是多的借鉴意义并值得咱们学习。

到最后,咱们仍是来看一些比较开放的相关问题,有兴趣的读者能够仔细思考一下下面的问题:

    • QUIC 协议是可否保证丢包率较高时的传输性能?

    • 除了 SACK 和 TFO 以外还有哪些手段能够优化 TCP 的性能?

相关文章
相关标签/搜索