三次握手
TCP 使用三次握手建立连接应该是全世界所有工程师都十分了解的知识点,三次握手的主要目的是避免历史错误连接的建立并让通信的双方确定初始序列号12,然而三次握手的成本相当高,在不丢包的情况下,它需要建立 TCP 连接的双方进行三次通信。
图 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 字节:
图 4 - TCP 三次握手的额外开销
虽然 TCP 不会为每一个发出的数据段建立连接,但是三次握手建立连接需要的成本还是相当高,不仅需要额外增加 1.5RTT 的网络延时,还需要增加 222 字节的额外开销,所以在弱网环境下,通过三次握手建立连接会加剧 TCP 的性能问题。
重传机制
TCP 传输的可靠性是通过序列号和接收方的 ACK 来保证的,当 TCP 传输一个数据段时,它会将该数据段的副本放到重传队列上并开启计时器14:
如果发送方收到了该数据段对应的 ACK 响应,当前数据段就会从重传队列中删除;
如果发送方在计时器到期之间都没有收到该数据段对应的 ACK,就会重新发送当前数据段;
TCP 的 ACK 机制可能会导致发送方重新传输接收方已经收到了数据段。TCP 中的 ACK 消息表示该消息之前的全部消息都已经被成功接收和处理,例如:
发送方向接收方发送了序号为 1-10 的消息;
接收方向发送方发送 ACK 8 响应;
发送方认为序号为 1-8 的消息已经被成功接收;
这种 ACK 的方式在实现上比较简单,更容易保证消息的顺序性,但是在以下情况可能会导致发送方重传已经接收的数据:
图 5 - TCP 的重传策略
如上图所示,接收方已经收到了序号为 2-5 的数据,但是由于 TCP ACK 的语义是当前数据段前的全部数据段都已经被接收和处理,所以接收方无法发送 ACK 消息,由于发送方没有收到 ACK,所有数据段对应的计时器就会超时并重新传输数据。在丢包较为严重的网络下,这种重传机制会造成大量的带宽浪费。
总结
TCP 协议的一些设计在今天来看虽然仍然具有巨大的价值,但是并不能适用于所有场景。为了解决 TCP 的性能问题,目前业界有两种解决方案:
使用 UDP 构建性能更加优异、更灵活的传输协议,例如:QUIC15 等;
通过不同的手段优化 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 的性能?
如果对文章中的内容有疑问或者想要了解更多软件工程上一些设计决策背后的原因,可以在博客下面留言,作者会及时回复本文相关的疑问并选择其中合适的主题作为后续的内容。
TCP Selective Acknowledgment Options, October 1996 https://tools.ietf.org/html/rfc2018 ↩
KCP - A Fast and Reliable ARQ Protocol https://github.com/skywind3000/kcp ↩
Measuring Network Performance: Links Between Latency, Throughput and Packet Loss https://accedian.com/enterprises/blog/measuring-network-performance-latency-throughput-packet-loss/ ↩
Wikipedia: TCP congestion control https://en.wikipedia.org/wiki/TCP_congestion_control ↩
Wikipedia: Network congestion https://en.wikipedia.org/wiki/Network_congestion#Congestive_collapse ↩
Wikipedia: Additive increase/multiplicative decrease https://en.wikipedia.org/wiki/Additive_increase/multiplicative_decrease ↩
Bandwidth-delay product https://en.wikipedia.org/wiki/Bandwidth-delay_product ↩
TCP_INIT_CWND https://github.com/torvalds/linux/blob/738d2902773e30939a982c8df7a7f94293659810/include/net/tcp.h#L226 ↩
RFC2414 Increasing TCP’s Initial Window https://tools.ietf.org/html/rfc2414 ↩
RFC3390 Increasing TCP’s Initial Window https://tools.ietf.org/html/rfc3390 ↩
RFC6928 Increasing TCP’s Initial Window https://tools.ietf.org/html/rfc6928 ↩
为什么 TCP 建立连接需要三次握手, October 2019 https://draveness.me/whys-the-design-tcp-three-way-handshake ↩
Researchers create fiber network that operates at 99.7% speed of light, smashes speed and latency records, March 2013 https://www.extremetech.com/computing/151498-researchers-create-fiber-network-that-operates-at-99-7-speed-of-light-smashes-speed-and-latency-records ↩
RFC793 Transmission Control Protocol, September 1981 RFC793 https://tools.ietf.org/html/rfc793 ↩
Wikiepedia: QUIC https://en.wikipedia.org/wiki/QUIC ↩
RFC018 TCP Selective Acknowledgment Options, October 1996 https://tools.ietf.org/html/rfc2018 ↩
RFC7413 TCP Fast Open, December 2014 https://tools.ietf.org/html/rfc7413 ↩
HTTP-over-QUIC to be renamed HTTP/3, November 2018 https://www.zdnet.com/article/http-over-quic-to-be-renamed-http3/ ↩
本文转载自 Draveness 技术博客。
原文链接:https://draveness.me/whys-the-design-tcp-performance
评论