TCP三次握手与四次挥手

TCP(Transmission Control Protocol,传输控制协议)是互联网协议栈中传输层的核心协议之一,它提供面向连接的、可靠的、基于字节流的通信。要理解TCP的工作原理,最重要的两个过程就是三次握手 (建立连接)和四次挥手(释放连接)。本文将结合经典的报文交换图,深入解析这两个过程,并解答常见疑问(如为什么是三次?为什么是四次?TIME_WAIT有什么用?)。

一、TCP基础概念回顾

在深入握手挥手之前,先了解几个关键字段:

  • 序列号(seq):占4字节,表示本报文段所发送数据的第一个字节的序号。在建立连接时,双方会随机初始化一个序号(ISN)。

  • 确认号(ack):占4字节,仅在ACK标志位为1时有效,表示期望收到对方下一个报文段数据的第一个字节的序号,通常为上次收到对方数据的最后一个序号加1。

  • 标志位

    • SYN:同步序号,用于建立连接。

    • ACK:确认,表示确认号字段有效。

    • FIN:结束,用于释放连接。

    • 其他标志(RST、PSH、URG)本文不展开。

二、三次握手:建立可靠的连接

三次握手的主要目的是:确保双方都具备收发能力,并同步初始序列号。过程如下图所示(结合经典状态转移):

text

复制代码
客户端                             服务器
  |-------- SYN=1, seq=x ---------->|   (1)  SYN-SENT
  |<------ SYN=1, ACK=1, seq=y, ack=x+1 ----|   (2) SYN-RCVD
  |-------- ACK=1, seq=x+1, ack=y+1 ------->|   (3) 进入ESTABLISHED

第一步:客户端发送SYN报文

客户端主动打开连接,发送一个SYN标志位置1的报文段,其中包含一个随机初始化的序列号 seq = x。此时客户端进入 SYN-SENT 状态。

第二步:服务器回复SYN+ACK

服务器收到SYN后,如果同意建立连接,则回复一个SYN和ACK标志位均为1的报文段:

  • 确认号 ack = x + 1,表示期望收到客户端下一个字节序号为x+1的数据,同时确认了客户端的SYN。

  • 服务器也随机初始化自己的序列号 seq = y

    此时服务器进入 SYN-RCVD 状态。

第三步:客户端发送ACK确认

客户端收到服务器的SYN+ACK后,需要发送一个ACK报文段:

  • 确认号 ack = y + 1,表示收到服务器的SYN。

  • 序列号 seq = x + 1(因为之前发送的SYN不携带数据,所以序列号+1)。

    此时客户端进入 ESTABLISHED 状态。服务器收到这个ACK后也进入 ESTABLISHED 状态,连接建立完成。

为什么必须是三次握手?

  • 防止已失效的连接请求突然到达服务器:如果客户端发出的第一个SYN因为网络延迟,在连接释放后才到达服务器,服务器误以为新请求并回复SYN+ACK。如果只有两次握手,服务器就会建立连接并等待数据,浪费资源。而三次握手时,客户端不会对服务器的SYN+ACK再发送确认,服务器收不到最后的ACK,就不会建立连接。

  • 确保双方收发能力正常:第一次握手证明客户端的发送能力和服务器的接收能力;第二次握手证明服务器的发送能力和客户端的接收能力;第三次握手确认双方都已就绪。

  • 同步初始序列号:双方需要交换初始序列号,以便后续数据的有序传输。

三、四次挥手:优雅地释放连接

TCP是全双工协议,即两个方向的数据传输可以独立关闭。因此释放连接时需要分别关闭每个方向,过程通常需要四次交互。假设客户端主动关闭连接:

text

复制代码
主动方(客户端)                      被动方(服务器)
  |-------- FIN=1, seq=u -------->|   (1) FIN-WAIT-1
  |<------ ACK=1, seq=v, ack=u+1 -----|   (2) CLOSE-WAIT
  (此时进入FIN-WAIT-2)
  |<------ FIN=1, ACK=1, seq=w, ack=u+1 ----|   (3) LAST-ACK
  |-------- ACK=1, seq=u+1, ack=w+1 ------>|   (4) TIME-WAIT (2MSL后关闭)

第一步:主动方发送FIN

主动方(如客户端)发送一个FIN标志位置1的报文段,序列号为 seq = u(等于之前发送数据的最后一个字节的序号+1)。此时主动方进入 FIN-WAIT-1 状态,表示自己不再发送数据。

第二步:被动方回复ACK

被动方收到FIN后,发送一个ACK报文段:

  • 确认号 ack = u + 1

  • 序列号 seq = v(等于自己发送数据的最后一个字节序号+1)。

    被动方进入 CLOSE-WAIT 状态。主动方收到ACK后进入 FIN-WAIT-2 状态,等待被动方关闭连接。此时被动方可能还有数据要发送,所以主动方仍需接收数据。

第三步:被动方发送FIN

被动方处理完剩余数据后,也发送一个FIN报文段(可携带ACK标志):

  • FIN=1, ACK=1。

  • 序列号 seq = w(可能基于之前的v,但通常w=v,因为数据可能已经发送完毕)。

  • 确认号仍为 ack = u+1

    被动方进入 LAST-ACK 状态,等待最后的ACK。

第四步:主动方回复ACK

主动方收到FIN后,发送一个ACK报文段:

  • 确认号 ack = w+1

  • 序列号 seq = u+1

    此时主动方进入 TIME-WAIT 状态,等待2MSL(最大报文段生存时间)后进入 CLOSED 状态。被动方收到ACK后立即进入 CLOSED 状态。

为什么必须是四次挥手?

  • 全双工关闭的独立性:当主动方发送FIN时,仅表示自己不再发送数据,但被动方可能还有数据未发送完,所以不能立即关闭。被动方先发送ACK表示收到关闭请求,待数据发送完毕后再发送自己的FIN。因此ACK和FIN通常分开发送,形成四次。

  • 特殊情况:如果被动方在收到FIN后已经没有数据要发送,可以将ACK和FIN合并在一个报文段中发送,从而变成三次挥手(即第二、三步合并)。但标准实现仍建议分开发送,以确保兼容性和可靠性。

为什么需要TIME-WAIT状态?

TIME-WAIT状态持续2MSL(MSL为报文段最大生存时间,通常为30秒、1分钟或2分钟),主要作用:

  1. 确保最后的ACK能被对方收到:如果最后的ACK丢失,被动方会超时重发FIN,主动方在TIME-WAIT状态下可以重发ACK,保证连接可靠关闭。

  2. 防止旧报文干扰新连接:等待足够时间让所有属于旧连接的报文段在网络中消失,避免它们被后续使用相同端口的新连接错误接收。

四、常见问题与深入探讨

1. 三次握手可以携带数据吗?

第一次和第二次握手不能携带数据,因为此时连接尚未建立,携带数据会被对端丢弃(或导致安全隐患)。第三次握手可以携带数据,因为此时客户端已确认服务器有接收能力,且服务器一旦收到数据就会交给应用层。

2. 为什么初始序列号(ISN)要随机?

为了防止历史报文段被误认为是新连接的报文。如果ISN固定,攻击者可能伪造RST重置连接。随机化ISN(如RFC 1948中建议的算法)可提高安全性。

3. SYN Flood攻击是什么?如何防范?

攻击者伪造大量源IP发送SYN报文,但不完成第三次握手,导致服务器维护大量半连接(SYN-RCVD状态),耗尽资源。防范方法:

  • SYN Cookie:服务器不直接保存半连接信息,而是将连接参数加密后作为初始序列号(Cookie)在SYN+ACK中返回。收到ACK后验证Cookie,合法才分配资源。

  • 增加半连接队列大小、缩短超时时间等。

4. TIME-WAIT为什么是2MSL?

MSL是报文段在网络中的最大生存时间。主动方发送ACK后,这个ACK可能丢失,被动方会重传FIN。主动方需要等待足够时间(至少1MSL)接收重传的FIN,并再发送ACK(又需要1MSL)。因此2MSL能保证:

  • 最后一个ACK到达对端(如果丢失,对端重传的FIN到达主动方,主动方重置2MSL计时器并重发ACK)。

  • 本连接的所有报文段从网络中消失。

5. 如果连接双方同时打开或同时关闭?

TCP设计允许同时打开(双方同时发送SYN)和同时关闭(双方同时发送FIN)。同时打开需要四次握手,但通过特殊处理可以建立连接;同时关闭需要四次挥手,但会进入类似FIN-WAIT的状态。实际应用中很少见,但TCP规范支持。

五、总结

TCP的三次握手和四次挥手是网络通信的基石。理解它们不仅有助于面试和考试,更能帮助我们在网络编程中排查问题(如连接建立缓慢、大量TIME-WAIT、半连接耗尽等)。本文结合经典的状态迁移和报文交换,从原理到常见问题做了详细解析。希望读者能通过抓包工具(如Wireshark)亲自观察这些过程,加深理解。

最后,如果你对网络协议感兴趣,推荐阅读《TCP/IP详解 卷1》和RFC 793,那里有更全面的描述。

相关推荐
是孑然呀2 小时前
【笔记】openclaw+飞书多agent(非群聊方式)
笔记·飞书
鸽子一号2 小时前
c#之常用的字符串操作
笔记
Kiyra2 小时前
[特殊字符] LeetCode 做题笔记(二):678. 有效的括号字符串
笔记·算法·leetcode
LeeeX!2 小时前
玩转 3D 检测和分割(一):MMDetection3D 整体框架介绍
笔记·多模态感知
ysa0510302 小时前
贪心【逆向dp】
数据结构·c++·笔记·算法
【数据删除】3483 小时前
计算机复试学习笔记 Day43
笔记·学习
深瞳智检3 小时前
lesson-01 NLP 概述学习笔记 & 学习心得
人工智能·笔记·学习·自然语言处理·llm·大语言模型
bestadc3 小时前
Hello-Agents 第一章 初识智能体 学习笔记
笔记·学习
次旅行的库3 小时前
【问渠哪得清如许-数据分析】学习笔记-上
数据库·笔记·sql·学习·postgresql·数据分析