# 服务器为什么会发 RST

下面这部分我会**严格从 TCP 协议与 Linux 内核实现角度**来讲“**服务器为什么会发 RST**”，不做泛泛而谈，给你的是**能用于抓包分析、问题定性、对外解释的判断逻辑**。

---

# 一、一句话总览（给结论）

> **服务器发送 RST，不是“情绪行为”，而是 TCP 状态机在发现“当前报文与连接状态不一致”时的强制纠错机制。**

换句话说：

> **RST = “这个报文在我看来是非法的 / 不可能的 / 不被接受的”**

---

# 二、TCP 中 RST 的协议级定义

根据 **RFC 793 / RFC 1122**：

服务器**必须发送 RST**的典型条件：

1. 收到一个 **不存在连接的报文**
2. 收到一个 **序列号不可接受的报文**
3. 收到一个 **状态不合法的报文**
4. 应用层明确要求立即中断连接

RST 的作用只有一个：

> **立即终止 TCP 连接并释放所有状态**

---

# 三、服务器“主动”发送 RST 的核心判断逻辑（按内核顺序）

以下逻辑可理解为 **Linux TCP 内核的决策流程**。

---

## 1️⃣ 收到报文后，先查“连接是否存在”

### 判断点

- 是否能在内核 TCP 连接表中找到：
    
    ```
    src IP + src port + dst IP + dst port
    
    ```

### 结果

<table id="bkmrk-%E7%BB%93%E6%9E%9C-%E8%A1%8C%E4%B8%BA-%E6%89%BE%E5%88%B0-%E8%BF%9B%E5%85%A5%E4%B8%8B%E4%B8%80%E6%AD%A5-%E6%9C%AA%E6%89%BE%E5%88%B0-%E7%9B%B4"><thead><tr><th>结果</th><th>行为</th></tr></thead><tbody><tr><td>找到</td><td>进入下一步</td></tr><tr><td>未找到</td><td>**直接回 RST**</td></tr></tbody></table>

### 抓包特征

- RST 通常带 ACK
- ACK = 对方 seq + len

---

## 2️⃣ 连接存在，但 TCP 状态是否合法？

### 状态机合法性校验

例如：

<table id="bkmrk-%E5%BD%93%E5%89%8D%E7%8A%B6%E6%80%81-%E6%94%B6%E5%88%B0%E6%8A%A5%E6%96%87-%E8%A1%8C%E4%B8%BA-establi"><thead><tr><th>当前状态</th><th>收到报文</th><th>行为</th></tr></thead><tbody><tr><td>ESTABLISHED</td><td>正常 ACK</td><td>继续</td></tr><tr><td>ESTABLISHED</td><td>SYN</td><td>**RST**</td></tr><tr><td>FIN\_WAIT</td><td>数据包</td><td>**RST**</td></tr><tr><td>CLOSED</td><td>任意</td><td>**RST**</td></tr></tbody></table>

---

## 3️⃣ 序列号（Sequence Number）是否在接收窗口内？

### 判断点

- `seq` 是否在：
    
    ```
    rcv_nxt ≤ seq < rcv_nxt + rcv_wnd
    
    ```

### 不在窗口内

- 被视为 **无效或重放报文**
- **直接 RST**

> ⚠️ NAT 重映射、出口 IP 变更最容易触发这一点

---

## 4️⃣ TCP Flag 组合是否合法？

非法组合示例：

- SYN + FIN
- SYN + RST
- FIN + 数据 + 非法 seq

处理方式：

- **直接 RST**

---

## 5️⃣ 应用层主动要求“硬断”

这不是协议自动行为，而是程序逻辑。

### 常见方式

- `close()` + `SO_LINGER=0`
- 程序 crash
- 程序主动拒绝连接

结果：

- 内核直接发 RST
- 不进入 FIN/TIME\_WAIT

---

## 6️⃣ 安全 / 防火墙策略触发

虽然你看到的是“服务器 IP 发 RST”，但实际上可能是：

- iptables REJECT --reject-with tcp-reset
- conntrack 认为连接非法
- SYN flood 防护

---

# 四、结合你当前场景的“精准判断模型”

你描述的是：

> **通信一段时间后 → 服务器发 RST → 设备离线**

结合上面的逻辑，**最符合的是以下两类判断**：

---

## ✅ 场景 A（最高概率）：连接存在，但状态不一致

触发点：

- 运营商出口 IP 变更
- NAT 会话刷新
- 中间设备丢失状态

服务器看到的是：

> “这是一个我记得的连接，但你发来的 seq / ACK 对不上了”

→ **RST**

---

## ✅ 场景 B：服务器认为“这个连接已经不存在”

触发点：

- 长时间无数据
- 中间链路丢包
- keepalive 过长

服务器 TCP 表中：

> 连接已被回收

终端仍在发数据：

→ **RST**

---

## ❌ 最不可能的场景

- 服务器 BUG
- 内核异常
- 网络卡问题

如果是这些：

- RST 会随机出现
- 不会与“出口 IP 变更”强相关

---

# 五、抓包中“如何一眼判断 RST 原因”（实战）

### 1️⃣ RST 是否带 ACK？

<table id="bkmrk-%E7%89%B9%E5%BE%81-%E7%BB%93%E8%AE%BA-rst-%2B-ack-%E5%AF%B9%E6%96%B9%E8%BF%9E%E6%8E%A5"><thead><tr><th>特征</th><th>结论</th></tr></thead><tbody><tr><td>RST + ACK</td><td>对方连接存在，但不接受当前状态</td></tr><tr><td>纯 RST</td><td>连接根本不存在</td></tr></tbody></table>

---

### 2️⃣ RST 的 ACK 值是否“合理”？

```text
ACK = 对方最后一个 seq + len

```

- 合理 → 状态机判定
- 乱值 → 防火墙代发

---

### 3️⃣ TTL 是否异常？

- TTL 与正常数据包一致 → 服务器内核
- TTL 明显更小 / 不一致 → 中间设备

---

# 六、为什么“服务器重启不解决问题”（用这个逻辑解释）

服务器重启后：

- TCP 连接表清空
- 终端仍在旧状态
- 第一包数据 → “连接不存在”
- **立刻 RST**

所以：

> **重启服务器只会制造更多 RST，不会减少**

---

# 七、一句话总结（给你做结论用）

> **服务器发送 RST 的本质，是 TCP 状态机在发现连接状态不一致或非法报文时，执行的强制清理动作；它反映的是链路或会话状态问题，而非服务器主动异常。**

---

如果你愿意，下一步我可以：

- 按你现有抓包 **逐字段解析 RST 报文**
- 给你一份 **RST 排障速查表（字段级）**
- 帮你把这些内容整理成 **客户可读的技术解释版本**

你只要告诉我：  
👉 **是否有抓包（pcap）或截图？**