TCP三次握手四次挥手

tcp三次握手

TCP(传输控制协议)的三次握手是建立可靠连接的关键过程,用于在客户端和服务器之间同步序列号并交换控制信息


三次握手的过程(简化版)

  1. 第一次握手(SYN)

    • 发送方: 客户端
    • 数据包内容: 客户端发送一个 TCP 数据包,设置 SYN(同步)标志位为 1,并携带一个初始序列号(Sequence Number),通常记为 Seq = x
    • 含义: 表示客户端请求建立连接,并告诉服务器客户端的初始序列号。
    • 状态: 客户端进入 SYN_SENT 状态。
  2. 第二次握手(SYN+ACK)

    • 发送方: 服务器
    • 数据包内容: 服务器回应一个 TCP 数据包,设置 SYN 标志位为 1ACK(确认)标志位为 1
      • 服务器的初始序列号为 Seq = y
      • 确认号(Acknowledgment Number)为 Ack = x + 1,表示服务器已接收到客户端的 SYN 并期待客户端的下一个数据包序列号。
    • 含义: 服务器同意建立连接,同时告知客户端自己的初始序列号。
    • 状态: 服务器进入 SYN_RCVD 状态。
  3. 第三次握手(ACK)

    • 发送方: 客户端
    • 数据包内容: 客户端发送一个 TCP 数据包,设置 ACK 标志位为 1
      • 序列号 Seq = x + 1(紧接第一次握手的序列号)。
      • 确认号 Ack = y + 1,表示客户端已接收到服务器的 SYN 并确认。
    • 含义: 客户端确认服务器的响应,连接正式建立。
    • 状态: 客户端和服务器都进入 ESTABLISHED 状态。

数据包示例(简化为关键字段)

假设客户端初始序列号为 1000,服务器初始序列号为 2000:

  1. 客户端 -> 服务器:
    • SYN = 1, Seq = 1000
  2. 服务器 -> 客户端:
    • SYN = 1, ACK = 1, Seq = 2000, Ack = 1001
  3. 客户端 -> 服务器:
    • ACK = 1, Seq = 1001, Ack = 2001

d516fa6c61915d44e09f9cffcd2ee95c.png

总结

三次握手中的数据包通过 SYNACK 标志位以及序列号、确认号的交换,确保双方能够可靠地初始化通信。完成后,TCP 连接建立,后续数据传输即可开始。

抓包分析三次握手

  • sudo tcpdump -i en0
  • wireshark

69a1859c1c910d72868d93fe177eb8e3.png

1.src -> dst syn(syn_sent)

  • flag=syn syn_sent(src)
  • SEQ=0 相对序列号
  • Seq=x0577 原始序列号

9c56a5a20a831beed44a9be7f39ca1d2.png

2.dst -> src syn + ack(syn_rcvd)

  • flag:sync + ack syn_rcvd(dst)
  • SEQ=0, Seq=x0888
  • Ack=0578

f4c7c180876a848cd15bd739685380aa.png

3. src -> dst ack(established)

  • flag: ack established(src/dst)
  • ACK=1
  • Seq=x0578 Ack=0x889

a3bc556aa6abfe7d8616aa78bd1a311f.png

三次握手详解步骤

  1. 第一次握手(SYN,客户端 → 服务器)

    • 客户端发送一个 SYN(同步)报文给服务器。
    • 这个报文中包含客户端的初始序列号(Sequence Number,通常记为 seq = x),表示客户端数据流的起始点。
    • 标志位:SYN = 1,表示请求建立连接。
    • 此时客户端进入 SYN_SENT 状态,等待服务器的响应。
  2. 第二次握手(SYN + ACK,服务器 → 客户端)

    • 服务器收到客户端的 SYN 后,回复一个 SYN + ACK 报文。
    • 这个报文中:
      • SYN = 1:表示服务器也请求同步。
      • ACK = 1:确认收到客户端的 SYN。
      • 服务器的初始序列号(seq = y),表示服务器数据流的起始点。
      • 确认号(Acknowledgment Number,记为 ack = x + 1),表示服务器期望接收客户端的下一个序列号。
    • 服务器进入 SYN_RCVD(同步收到)状态。
  3. 第三次握手(ACK,客户端 → 服务器)

    • 客户端收到服务器的 SYN + ACK 后,发送一个 ACK 报文确认。
    • 这个报文中:
      • ACK = 1:确认收到服务器的 SYN。
      • 序列号(seq = x + 1),与服务器的确认号对应。
      • 确认号(ack = y + 1),表示客户端期望接收服务器的下一个序列号。
    • 客户端进入 ESTABLISHED(连接建立)状态。
    • 服务器收到这个 ACK 后,也进入 ESTABLISHED 状态。

图示

客户端                     服务器
   |    ---- SYN (seq=x) ---->   |
   |    <--- SYN+ACK (seq=y, ack=x+1) ---   |
   |    ---- ACK (seq=x+1, ack=y+1) --->   |
   |           [连接建立]          |

d20be446bb65d3a4797849ec61a8c39b.png

为什么要三次握手?

三次握手的主要目的是:

  1. 确认双向通信能力:确保客户端和服务器都能发送和接收数据。
  2. 同步序列号:双方交换初始序列号(x 和 y),为后续数据传输建立可靠的顺序。
  3. 防止旧连接干扰:避免因网络延迟导致的过期 SYN 报文误建立连接。

举个生活中的类比

想象你在打电话:

  1. 你拨号并说:“喂,你能听见我吗?”(第一次握手,SYN)
  2. 对方的电话接通,说:“能听见,你能听见我吗?”(第二次握手,SYN + ACK)
  3. 你回答:“能听见,开始聊吧!”(第三次握手,ACK)

常见问题

  • 能不能用两次握手? 不行。两次握手只能确认客户端到服务器的通信,服务器到客户端的确认无法保证,可能导致单向连接或旧连接干扰。
  • 三次握手中途失败会怎样? 如果某一步失败(比如服务器没回应),客户端会超时重试(通常基于 TCP 重传机制),最终放弃并关闭连接尝试。

查看三次握手

在 Linux 中可以用 tcpdumpwireshark 抓包观察:

1
sudo tcpdump -i eth0 port 80
  • 假设你在监听 80 端口的 HTTP 连接,就能看到 SYN、SYN+ACK 和 ACK 的报文。

四次挥手关闭

TCP 的四次挥手(Four-Way Handshake)是终止 TCP 连接的过程,用于确保客户端和服务器双方都能安全地关闭连接,并确认所有数据都已被正确传输。以下是 TCP 四次挥手的详细过程,包括每个数据包的角色和状态变化。


四次挥手的过程

TCP 四次挥手涉及客户端和服务器之间的四次数据包交换,标志位(如 FIN 和 ACK)用于控制连接的关闭。假设客户端主动发起关闭,以下是步骤:

  1. 第一次挥手(FIN)

    • 发送方: 客户端
    • 数据包内容: 客户端发送一个 TCP 数据包,设置 FIN(结束)标志位为 1,序列号为 Seq = x
    • 含义: 客户端表示自己没有更多数据要发送,请求关闭连接。
    • 状态: 客户端进入 FIN_WAIT_1 状态。
  2. 第二次挥手(ACK)

    • 发送方: 服务器
    • 数据包内容: 服务器回应一个 TCP 数据包,设置 ACK(确认)标志位为 1,确认号为 Ack = x + 1,序列号为 Seq = y(服务器当前的序列号)。
    • 含义: 服务器确认收到客户端的 FIN,告诉客户端“我已知道你要关闭”。但服务器可能仍有数据要发送,因此此时连接尚未完全关闭。
    • 状态:
      • 客户端收到 ACK 后,从 FIN_WAIT_1 进入 FIN_WAIT_2 状态。
      • 服务器进入 CLOSE_WAIT 状态。
  3. 第三次挥手(FIN)

    • 发送方: 服务器
    • 数据包内容: 服务器在完成数据发送后,发送一个 TCP 数据包,设置 FIN 标志位为 1,序列号为 Seq = z,确认号仍为 Ack = x + 1(可能与第二次挥手相同)。
    • 含义: 服务器表示自己也没有更多数据要发送,同意关闭连接。
    • 状态: 服务器进入 LAST_ACK 状态。
  4. 第四次挥手(ACK)

    • 发送方: 客户端
    • 数据包内容: 客户端回应一个 TCP 数据包,设置 ACK 标志位为 1,序列号为 Seq = x + 1,确认号为 Ack = z + 1
    • 含义: 客户端确认收到服务器的 FIN,连接正式关闭。
    • 状态:
      • 客户端进入 TIME_WAIT 状态,等待 2MSL(两倍的最大段生存时间,通常为 30 秒到 2 分钟),确保网络中残留的数据包消失后,进入 CLOSED 状态。
      • 服务器收到 ACK 后,直接进入 CLOSED 状态。

四次挥手的图示

假设客户端初始序列号为 x,服务器初始序列号为 y,以下是过程:

客户端              服务器
  |                   |
  | FIN (Seq=x)      |  ----> 第一次挥手
  |----------------->|
  |                   |
  | ACK (Seq=y, Ack=x+1) |  <---- 第二次挥手
  |<-----------------|
  |                   |
  | FIN (Seq=z, Ack=x+1) |  <---- 第三次挥手
  |<-----------------|
  |                   |
  | ACK (Seq=x+1, Ack=z+1) |  ----> 第四次挥手
  |----------------->|
  |                   |

6f532c71e8e19a301cf63a2451241fc4.png


为什么是四次挥手而不是三次?

  • TCP 是全双工协议,客户端和服务器都可以独立发送数据。因此,关闭连接时需要双方分别发送 FIN 并得到确认。
  • 三次握手是为了建立同步,而四次挥手是因为关闭时服务器可能还有剩余数据要发送,无法将自己的 FIN 和 ACK 合并为一个数据包

状态变化总结

  • 客户端: ESTABLISHED → FIN_WAIT_1 → FIN_WAIT_2 → TIME_WAIT → CLOSED
  • 服务器: ESTABLISHED → CLOSE_WAIT → LAST_ACK → CLOSED

使用 tcpdump 捕获四次挥手

可以用以下命令捕获四次挥手的数据包:

1
sudo tcpdump -i en0 -n port 80 or port 443

在捕获到的数据中,查找 [FIN][ACK] 标志的数据包即可看到四次挥手的过程。