Skip to content

题目

请简述 TCP 三次握手和四次挥手的过程。

📝 标准答案

核心要点

  1. 三次握手(建立连接)

    • 第一次:客户端发送 SYN
    • 第二次:服务器发送 SYN + ACK
    • 第三次:客户端发送 ACK
  2. 四次挥手(断开连接)

    • 第一次:客户端发送 FIN
    • 第二次:服务器发送 ACK
    • 第三次:服务器发送 FIN
    • 第四次:客户端发送 ACK
  3. 为什么需要三次握手

    • 确认双方的收发能力
    • 防止已失效的连接请求突然又传到服务器
    • 同步序列号
  4. 为什么需要四次挥手

    • TCP 是全双工通信
    • 需要双方都确认关闭
    • 服务器可能还有数据要发送

详细说明

三次握手过程

客户端                                服务器
  |                                    |
  |  1. SYN (seq=x)                    |
  |----------------------------------->|
  |     客户端:我要建立连接             |
  |                                    |
  |  2. SYN-ACK (seq=y, ack=x+1)       |
  |<-----------------------------------|
  |     服务器:收到,我也准备好了        |
  |                                    |
  |  3. ACK (ack=y+1)                  |
  |----------------------------------->|
  |     客户端:收到,开始传输数据        |
  |                                    |
  |  连接建立,开始传输数据               |
  |<=================================>|

详细过程:

  1. 第一次握手(SYN)

    • 客户端发送 SYN 包(seq=x)到服务器
    • 客户端进入 SYN_SENT 状态
    • 表示:客户端请求建立连接
  2. 第二次握手(SYN-ACK)

    • 服务器收到 SYN 包
    • 发送 SYN-ACK 包(seq=y, ack=x+1)
    • 服务器进入 SYN_RCVD 状态
    • 表示:服务器确认收到,并同意建立连接
  3. 第三次握手(ACK)

    • 客户端收到 SYN-ACK 包
    • 发送 ACK 包(ack=y+1)
    • 客户端和服务器都进入 ESTABLISHED 状态
    • 表示:连接建立成功,可以开始传输数据

四次挥手过程

客户端                                服务器
  |                                    |
  |  1. FIN (seq=u)                    |
  |----------------------------------->|
  |     客户端:我要关闭连接             |
  |                                    |
  |  2. ACK (ack=u+1)                  |
  |<-----------------------------------|
  |     服务器:收到,等我发完数据        |
  |                                    |
  |  3. FIN (seq=w)                    |
  |<-----------------------------------|
  |     服务器:我也要关闭连接了         |
  |                                    |
  |  4. ACK (ack=w+1)                  |
  |----------------------------------->|
  |     客户端:收到,再见               |
  |                                    |
  |  连接关闭                           |

详细过程:

  1. 第一次挥手(FIN)

    • 客户端发送 FIN 包(seq=u)
    • 客户端进入 FIN_WAIT_1 状态
    • 表示:客户端没有数据要发送了
  2. 第二次挥手(ACK)

    • 服务器收到 FIN 包
    • 发送 ACK 包(ack=u+1)
    • 服务器进入 CLOSE_WAIT 状态
    • 客户端进入 FIN_WAIT_2 状态
    • 表示:服务器确认收到,但可能还有数据要发送
  3. 第三次挥手(FIN)

    • 服务器发送完数据后,发送 FIN 包(seq=w)
    • 服务器进入 LAST_ACK 状态
    • 表示:服务器也没有数据要发送了
  4. 第四次挥手(ACK)

    • 客户端收到 FIN 包
    • 发送 ACK 包(ack=w+1)
    • 客户端进入 TIME_WAIT 状态(等待 2MSL)
    • 服务器收到 ACK 后进入 CLOSED 状态
    • 客户端等待 2MSL 后进入 CLOSED 状态
    • 表示:连接完全关闭

🧠 深度理解

为什么需要三次握手?

1. 确认双方的收发能力

第一次握手:客户端发送能力 OK,服务器接收能力 OK
第二次握手:服务器发送能力 OK,客户端接收能力 OK
第三次握手:确认双方收发能力都 OK

2. 防止已失效的连接请求

场景:
1. 客户端发送 SYN1(因网络延迟,未到达)
2. 客户端超时,重新发送 SYN2
3. SYN2 到达,建立连接,传输数据,关闭连接
4. SYN1 延迟后到达服务器

如果只有两次握手:
- 服务器收到 SYN1,发送 SYN-ACK
- 服务器认为连接建立,等待数据
- 客户端不理会(已关闭),造成资源浪费

有三次握手:
- 服务器发送 SYN-ACK
- 客户端不回复 ACK
- 服务器超时,关闭连接

3. 同步序列号

- 序列号用于保证数据包的顺序
- 双方需要交换初始序列号
- 三次握手确保双方都知道对方的序列号

为什么需要四次挥手?

TCP 是全双工通信:

客户端 → 服务器:数据通道1
客户端 ← 服务器:数据通道2

关闭连接需要关闭两个通道:
1. 客户端关闭发送通道(FIN)
2. 服务器确认(ACK)
3. 服务器关闭发送通道(FIN)
4. 客户端确认(ACK)

为什么不能合并成三次?

- 服务器收到 FIN 后,可能还有数据要发送
- 需要先发送 ACK 确认,然后继续发送数据
- 数据发送完毕后,再发送 FIN

TIME_WAIT 状态

为什么需要 TIME_WAIT?

1. 确保最后的 ACK 能够到达服务器
   - 如果 ACK 丢失,服务器会重发 FIN
   - 客户端需要等待,以便重发 ACK

2. 确保旧连接的数据包都已消失
   - 防止旧连接的数据包影响新连接
   - 等待 2MSL(Maximum Segment Lifetime)

TIME_WAIT 的问题:

- 占用端口资源
- 大量 TIME_WAIT 会导致端口耗尽
- 解决:调整系统参数,使用连接池

常见问题

1. 如果第三次握手失败?

- 服务器没收到 ACK,会重发 SYN-ACK
- 重发多次后,服务器关闭连接
- 客户端认为连接已建立,发送数据时会收到 RST

2. 如果第二次挥手失败?

- 客户端没收到 ACK,会重发 FIN
- 服务器收到重复的 FIN,会重发 ACK

3. 半关闭状态

- 客户端发送 FIN 后,不能再发送数据
- 但仍可以接收服务器的数据
- 这就是半关闭状态

💡 面试回答技巧

🎯 一句话回答(快速版)

三次握手:SYN → SYN-ACK → ACK,确认双方收发能力。四次挥手:FIN → ACK → FIN → ACK,因为 TCP 全双工需要双向关闭。

📣 口语化回答(推荐)

面试时可以这样回答:

"TCP 建立连接需要三次握手

第一次,客户端发送 SYN 包,告诉服务器我要建立连接,同时带上一个序列号。

第二次,服务器收到后发送 SYN-ACK 包,表示收到了,同时也带上自己的序列号。

第三次,客户端发送 ACK 包,确认收到服务器的响应。

为什么要三次?因为要确认双方的收发能力都正常。两次的话,服务器不知道客户端能不能收到自己的消息。

断开连接需要四次挥手

第一次,客户端发送 FIN,表示我不发数据了。

第二次,服务器发送 ACK,表示知道了。

第三次,服务器发送 FIN,表示我也不发了。

第四次,客户端发送 ACK,确认收到。

为什么是四次不是三次?因为 TCP 是全双工的,两个方向的数据传输要分别关闭。服务器收到客户端的 FIN 后,可能还有数据要发,所以 ACK 和 FIN 不能合并。

还有个 TIME_WAIT 状态,客户端发完最后一个 ACK 后要等 2MSL,确保服务器收到了,也让网络中残留的数据包消失。"

推荐回答顺序

  1. 先说三次握手

    • "第一次:客户端发送 SYN"
    • "第二次:服务器发送 SYN-ACK"
    • "第三次:客户端发送 ACK"
  2. 再说四次挥手

    • "第一次:客户端发送 FIN"
    • "第二次:服务器发送 ACK"
    • "第三次:服务器发送 FIN"
    • "第四次:客户端发送 ACK"
  3. 然后说原因

    • "三次握手:确认双方收发能力,防止失效连接"
    • "四次挥手:TCP 全双工,需要双向关闭"
  4. 最后说细节

    • TIME_WAIT 状态
    • 序列号的作用
    • 可能的问题

重点强调

  • 三次握手的必要性
  • 四次挥手不能合并的原因
  • TIME_WAIT 的作用
  • 全双工通信的特点

可能的追问

Q1: 为什么不是两次握手?

A: 两次握手无法确认客户端的接收能力,也无法防止已失效的连接请求。

Q2: 为什么不是四次握手?

A: 三次握手已经足够确认双方的收发能力,四次握手是多余的。

Q3: 什么是 SYN 洪水攻击?

A: 攻击者发送大量 SYN 包,但不回复 ACK,导致服务器维护大量半连接状态,耗尽资源。

防御:

  • SYN Cookie
  • 增加半连接队列大小
  • 减少 SYN-ACK 重试次数

Q4: 如何优化 TCP 连接?

A:

  • 使用 HTTP/2 多路复用
  • 启用 TCP Fast Open
  • 调整 TCP 参数(窗口大小、超时时间)
  • 使用连接池

💻 代码示例

模拟 TCP 连接

javascript
// 模拟 TCP 三次握手
class TCPConnection {
  constructor() {
    this.state = 'CLOSED';
    this.seq = Math.floor(Math.random() * 1000);
  }
  
  // 客户端:发起连接
  connect() {
    console.log('客户端:发送 SYN,seq=' + this.seq);
    this.state = 'SYN_SENT';
    
    // 模拟网络延迟
    setTimeout(() => {
      this.receiveSynAck();
    }, 100);
  }
  
  // 客户端:收到 SYN-ACK
  receiveSynAck() {
    console.log('客户端:收到 SYN-ACK');
    console.log('客户端:发送 ACK');
    this.state = 'ESTABLISHED';
    console.log('连接建立成功!');
  }
  
  // 客户端:关闭连接
  close() {
    console.log('客户端:发送 FIN');
    this.state = 'FIN_WAIT_1';
    
    setTimeout(() => {
      this.receiveAck();
    }, 100);
  }
  
  // 客户端:收到 ACK
  receiveAck() {
    console.log('客户端:收到 ACK');
    this.state = 'FIN_WAIT_2';
    
    setTimeout(() => {
      this.receiveFin();
    }, 100);
  }
  
  // 客户端:收到 FIN
  receiveFin() {
    console.log('客户端:收到 FIN');
    console.log('客户端:发送 ACK');
    this.state = 'TIME_WAIT';
    
    setTimeout(() => {
      this.state = 'CLOSED';
      console.log('连接关闭!');
    }, 200);  // 模拟 2MSL
  }
}

// 测试
const conn = new TCPConnection();
conn.connect();

setTimeout(() => {
  conn.close();
}, 1000);

🔗 相关知识点

📚 参考资料

基于 MIT 许可发布 | 隐私政策