🚨 TCP RST标志位:连接的"紧急刹车"

103 阅读3分钟

知识点编号:015
难度等级:⭐⭐(掌握)
面试频率:🔥🔥🔥


🎯 一句话总结

RST就像连接的紧急刹车,立即断开,不走四次挥手!🚨


🤔 什么是RST?

正常关闭:四次挥手
客户端                  服务器
  | FIN                  |
  |---------------------->|
  | ACK                  |
  |<----------------------|
  | FIN                  |
  |<----------------------|
  | ACK                  |
  |---------------------->|
  
需要4个包,温柔关闭

RST关闭:立即断开
客户端                  服务器
  | RST                  |
  |---------------------->|
  | 💥 连接立即断开!      |

只需1个包,暴力关闭

🎭 什么情况下发送RST?

1. 连接不存在

客户端                  服务器
  | SYN                  |
  |---------------------->|
  |                      | ❌端口未监听
  | RST                  |
  |<----------------------|

场景:访问一个没有监听的端口
示例:telnet localhost 99999999端口未开)
结果:Connection refused

2. 连接异常终止

// 直接关闭Socket,不走四次挥手
Socket socket = new Socket("localhost", 8080);
socket.setSoLinger(true, 0); // 设置SO_LINGER
socket.close(); // 发送RST,立即断开

// 效果:
// - 不等待发送缓冲区数据
// - 不等待四次挥手
// - 立即发RST断开

3. 接收到异常数据

连接已关闭,但收到数据:

客户端                  服务器
  | (连接已关闭)        |
  | 数据包                |
  |---------------------->|
  |                      | ❌连接不存在
  | RST                  |
  |<----------------------|

场景:
- 服务器重启,连接丢失
- 客户端不知道,继续发数据
- 服务器回复RST

4. 半连接队列满(SYN Flood防御)

SYN攻击时:

攻击者                  服务器
  | SYN                  |
  |---------------------->|
  |                      | 半连接队列满
  | RST                  |
  |<----------------------|

服务器拒绝连接,发送RST

💻 Java代码示例

触发RST

// 方式1:SO_LINGER = 0
Socket socket = new Socket("localhost", 8080);
socket.setSoLinger(true, 0); // 立即发送RST
socket.close(); // 发送RST,不是FIN

// 方式2:连接到未监听的端口
try {
    Socket socket = new Socket("localhost", 9999);
} catch (ConnectException e) {
    // Connection refused
    // 服务器发送了RST
    System.out.println("收到RST: " + e.getMessage());
}

// 方式3:在连接关闭后发送数据
Socket socket = new Socket("localhost", 8080);
socket.close();

try {
    socket.getOutputStream().write("data".getBytes());
} catch (IOException e) {
    // Socket closed
    System.out.println("连接已RST");
}

观察RST(使用tcpdump)

# 抓包观察RST
tcpdump -i any port 8080 -nn

# 输出示例:
# 10:00:00.001 IP 192.168.1.1.50000 > 192.168.1.2.8080: ... [SYN]
# 10:00:00.002 IP 192.168.1.2.8080 > 192.168.1.1.50000: ... [RST, ACK]
#                                                         ↑ RST标志

# Wireshark过滤器:
tcp.flags.reset == 1

⚠️ RST vs FIN 对比

特性FIN(正常关闭)RST(异常关闭)
过程四次挥手立即断开
数据等待发送完丢弃未发送数据
优雅温柔关闭暴力关闭
应用知道连接关闭可能收到异常
包数量4个包1个包
生活比喻:
FIN:好聚好散,先把话说完
RST:翻脸不认人,立即挂电话

🐛 常见面试题

Q1:什么是TCP RST?什么情况下会收到RST?

答案:

RST(Reset)是TCP的一个标志位,用于异常终止连接。

收到RST的场景:

1. ✅ 连接不存在
   - 访问未监听的端口
   - Connection refused

2. ✅ 连接异常关闭
   - SO_LINGER = 0
   - 程序崩溃

3. ✅ 接收到异常数据
   - 连接已关闭但收到数据
   - 服务器重启后收到旧连接数据

4. ✅ 防火墙拦截
   - 防火墙发送RST拒绝连接

5. ✅ 队列满
   - 半连接队列满
   - 全连接队列满

特点:
- 立即断开,不走四次挥手
- 丢弃未发送的数据
- 一个包完成断开

应用层表现:
- Java: SocketException
- C: Connection reset by peer

Q2:RST和FIN的区别?

答案:

FIN(正常关闭):
- 四次挥手,温柔关闭
- 等待数据发送完
- 对方知道你要关闭
- 优雅退出
- 适用:正常业务结束

RST(异常关闭):
- 立即断开,暴力关闭
- 丢弃未发送数据
- 对方可能收到异常
- 紧急刹车
- 适用:异常情况、拒绝连接

代码示例:
// FIN
socket.close(); // 默认四次挥手

// RST
socket.setSoLinger(true, 0);
socket.close(); // 发送RST

项目经验:
- 正常退出:用FIN
- 超时/异常:用RST
- 防止TIME_WAIT过多:用RST(谨慎)

🎓 总结

RST的关键点:

  1. 作用:异常终止连接
  2. 场景:端口未监听、异常数据、队列满
  3. 特点:立即断开,丢弃数据
  4. 对比FIN:暴力 vs 优雅

记忆口诀

RST紧急刹车 🚨
立即断开不墨迹 ⚡
端口未开会收到 🚫
异常数据也会来 ⚠️

文档创建时间:2025-10-31