当我们提到 TCP 协议时,最先想到的关键词是“面向连接”和“可靠”。TCP 协议设计的初衷是在客户端和服务器之间建立一个可靠的连接。在探讨连接过程之前,我们首先需要了解 TCP 报文段的结构。通过这个结构,我们可以知道 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,表明客户端确实收到了数据,至此,连接才算完全建立。
有了三次握手的基础,四次挥手就相对容易理解了:
为什么不能直接关闭连接呢?
关于 TIME_WAIT 状态还有许多有趣的内容,这里就不一一赘述了。希望本文能帮助你更好地理解 TCP 的工作原理。