TCP协议 - 三次握手,四次挥手和重要的细节(一)

图灵汇官网

TCP 报文段结构

当我们提到 TCP 协议时,最先想到的关键词是“面向连接”和“可靠”。TCP 协议设计的初衷是在客户端和服务器之间建立一个可靠的连接。在探讨连接过程之前,我们首先需要了解 TCP 报文段的结构。通过这个结构,我们可以知道 TCP 能够提供哪些信息:

  • TCP 协议通过一个四元组(源IP、源端口、目的IP、目的端口)来确定连接,这与 UDP 协议有所不同。IP地址位于 IP 报文段中,而 TCP 报文段本身不包含 IP 地址信息。
  • 基本 TCP 头部的长度是 20 字节,但由于“选项”的长度是不确定的,因此需要“首部长度”字段明确给出头部长度。需要注意的是,首部长度字段的单位是 32 位,即 4 字节,因此该字段的最小值为 5。
  • 标记为橙色的字段(如确认序号、接收窗口大小、ECE、ACK)用于回复对方。例如,服务器收到客户端的数据包后,不会单独发送一个数据包来回复,而是稍作等待,将确认信息附在下一个发往客户端的数据帧上,这就是“捎带”技术。
  • 窗口大小是一个 16 位无符号数,这意味着窗口被限制在了 65535 字节,从而限制了 TCP 的吞吐量功能。对于高速和高延迟的网络来说,这是一个问题。幸运的是,TCP 提供了“窗口缩放”(Window Scale)选项,允许对这个值进行缩放。

TCP 三次握手

三次握手的目的是在客户端和服务器之间建立连接。这个过程并不复杂,但其中有很多细节需要注意:

第一次握手

首先,客户端发起连接,发送一个 SYN 报文段(也称为段 1)。这个报文段中的 SYN 置位(即 SYN = 1),表明这是一个 SYN 报文段。这次发送的目的之一是告诉服务器客户端的初始序列号是 client_isn,还隐含地告诉服务器客户端想连接的端口号。除了这些,客户端还会发送一些选项,但这与三次握手无关,暂时不讨论。

段 1 中最需要关注的是 client_isn,即初始序列号。根据 RFC0793 的规定,当新建连接时,会使用一个初始序列号生成器来生成一个新的 32 位 ISN。这个生成器绑定在一个(可能是虚构的)32 位时钟上,其低阶位每大约 4 微秒增加一次。因此,ISN 的值每 4.55 小时循环一次。这个措施是为了防止序列号堆叠。

然而,即使如此,仍然存在安全隐患,因为初始 ISN 仍然是可预测的。恶意程序可以通过分析 ISN 来预测后续 TCP 连接的 ISN,并发动攻击。为了使初始序列号更难预测,现代系统通常使用半随机的方法来选择初始序列号,具体方法在此不展开讨论。

第二次握手

当服务器接收到客户端的连接请求后,会向客户端发送一个 ACK 报文段,表示服务器收到了连接请求,并且会把服务器的初始序列号告诉客户端。这两个步骤可以通过发送一个数据包来完成,这就是所谓的“捎带”技术。图中的 ACK = client_isn + 1 是指确认号字段的值,要注意与 ACK 标志位区分开。

当一个 SYN 报文段到达时,服务器会检查处于 SYNRCVD 状态的连接数量是否超过了 tcpmaxsynbacklog 参数,如果超过了,服务器就会拒绝连接。这种状态被称为“静默”。为了防御 SYN Flood 攻击,服务器可以使用“SYN cookies”,这种方法是当 SYN 到达时,不直接为其分配内存,而是把这条连接的信息编码并保存在 SYN-ACK 报文段的序列号字段中。如果客户端回复了,服务器再从 ACK 字段中解算出 SYN 报文的重要信息(有点像黑魔法的感觉),验证成功后才为该连接分配内存。这样,服务器不会响应攻击者的请求,正常连接则不会受到影响。

第三次握手

这是建立 TCP 连接的最后一步。经过前两次握手,客户端和服务器已经知道了对方的滑动窗口大小和初始序列号等信息。那么为什么还需要第三次握手呢?

这是因为服务器虽然已经发送了数据包,但它还不知道客户端是否收到了这个包。因此,服务器需要等待客户端返回一个 ACK,表明客户端确实收到了数据,至此,连接才算完全建立。

四次挥手

有了三次握手的基础,四次挥手就相对容易理解了:

  • 四次挥手的过程其实很简单,就是服务器和客户端互相发送 FIN 和 ACK 报文段,告知对方要断开连接。
  • 四次挥手中值得关注的一点是 TIME_WAIT 状态,即自动关闭连接的一方,在收到对方的 FIN 报文后,还要等待 2 MSL 的时间才会彻底关闭这条连接。(这里的 MSL 指的是最大段生存时间,即报文段在网络中被允许存在的最长时间。)

为什么不能直接关闭连接呢?

  • 一方面,第四次挥手的 ACK 报文段不一定到达了服务器,为了避免服务器一直处于 LAST_ACK 状态(服务器会重发 FIN,直到收到 ACK),客户端需要等待一段时间,看看是否需要重发。
  • 另一方面,经过 2 MSL 时间后,网络中与该连接相关的报文段都已消失,不会干扰新连接。例如,如果客户端向服务器建立了一个新的连接,旧连接中某些延迟的数据可能会持续到新连接建立完毕,而这些数据的序列号恰好还在滑动窗口内,服务器可能会误将其视为新连接的数据包接收。2 MSL 机制避免了这种情况的发生。

关于 TIME_WAIT 状态还有许多有趣的内容,这里就不一一赘述了。希望本文能帮助你更好地理解 TCP 的工作原理。

本文来源: 图灵汇 文章作者: 李佳怿