TCP 三次握手与四次挥手详解

发布于:2025-07-04 ⋅ 阅读:(20) ⋅ 点赞:(0)

一、TCP 连接建立(三次握手)

Client Server CLOSED → SYN_SENT SYN=1, seq=x (SYN包) CLOSED → SYN_RCVD SYN=1, ACK=1, seq=y, ack=x+1 (SYN-ACK包) SYN_SENT → ESTABLISHED ACK=1, seq=x+1, ack=y+1 (ACK包) SYN_RCVD → ESTABLISHED Client Server

详细过程:

  1. 第一次握手(SYN)

    • 客户端发送 SYN=1 标志(同步序列号)
    • 随机生成初始序列号 seq=x
    • 进入 SYN_SENT 状态
  2. 第二次握手(SYN-ACK)

    • 服务器收到 SYN 后,发送 SYN=1 和 ACK=1
    • 确认号 ack=x+1(期待收到 x+1)
    • 随机生成自己的序列号 seq=y
    • 进入 SYN_RCVD 状态
  3. 第三次握手(ACK)

    • 客户端确认收到 SYN-ACK
    • 发送 ACK=1,ack=y+1,seq=x+1
    • 双方进入 ESTABLISHED 状态

关键点:

  • 为什么需要三次?
    防止历史重复连接初始化导致的资源浪费(两次无法确认客户端接收能力)
  • 序列号作用
    解决网络包乱序、重复问题,保证可靠性

二、TCP 连接释放(四次挥手)

Client Server ESTABLISHED → FIN_WAIT_1 FIN=1, seq=u (FIN包) ESTABLISHED → CLOSE_WAIT ACK=1, ack=u+1 (ACK包) FIN_WAIT_1 → FIN_WAIT_2 FIN=1, seq=v, ack=u+1 (FIN包) CLOSE_WAIT → LAST_ACK ACK=1, ack=v+1, seq=u+1 (ACK包) FIN_WAIT_2 → TIME_WAIT → CLOSED LAST_ACK → CLOSED Client Server

详细过程:

  1. 第一次挥手(FIN)

    • 主动方发送 FIN=1,seq=u
    • 进入 FIN_WAIT_1 状态
  2. 第二次挥手(ACK)

    • 被动方发送 ACK=1,ack=u+1
    • 进入 CLOSE_WAIT 状态
    • 主动方收到后进入 FIN_WAIT_2
  3. 第三次挥手(FIN)

    • 被动方处理完数据后,发送 FIN=1,seq=v
    • 进入 LAST_ACK 状态
  4. 第四次挥手(ACK)

    • 主动方发送 ACK=1,ack=v+1
    • 进入 TIME_WAIT 状态(等待 2MSL)
    • 被动方收到后关闭连接

关键点:

  • 为什么需要四次?
    TCP 是全双工的,必须分别关闭两个方向的数据流
  • TIME_WAIT 状态
    • 等待 2MSL(Maximum Segment Lifetime,默认 60s)
    • 确保最后一个 ACK 到达对方
    • 让网络中残留的旧报文失效

三、状态机转换图

连接建立:

CLOSED → SYN_SENT → ESTABLISHED
CLOSED → SYN_RCVD → ESTABLISHED

连接释放:

ESTABLISHED → FIN_WAIT_1 → FIN_WAIT_2 → TIME_WAIT → CLOSED
ESTABLISHED → CLOSE_WAIT → LAST_ACK → CLOSED

四、常见问题

Q1:为什么不能用两次握手建立连接?

  • 可能造成历史重复连接占用服务器资源
  • 无法确认客户端的接收能力是否正常

Q2:TIME_WAIT 状态过多的危害?

  • 占用端口资源(高并发时可能耗尽端口)
  • 解决方案:
    • 调整 net.ipv4.tcp_tw_reuse 参数
    • 使用 SO_REUSEADDR 套接字选项

Q3:如果最后一次 ACK 丢失会怎样?

  • 被动方会重传 FIN 包
  • 主动方在 TIME_WAIT 期间能响应这些重传

五、Wireshark 抓包示例

三次握手:

1. [SYN] Seq=0
2. [SYN, ACK] Seq=0, Ack=1
3. [ACK] Seq=1, Ack=1

四次挥手:

1. [FIN, ACK] Seq=1, Ack=1
2. [ACK] Seq=1, Ack=2
3. [FIN, ACK] Seq=1, Ack=2
4. [ACK] Seq=2, Ack=2

六、编程实现(Python 伪代码)

# 服务端
def server():
    s = socket.socket()
    s.bind(('0.0.0.0', 80))
    s.listen()
    conn, addr = s.accept()  # 完成三次握手
    conn.close()  # 触发四次挥手

# 客户端
def client():
    s = socket.socket()
    s.connect(('server', 80))  # 发起三次握手
    s.close()  # 发起四次挥手

网站公告

今日签到

点亮在社区的每一天
去签到