【Java EE】 TCP—异常情况处理

异常情况处理

异常情况处理

进程崩溃

当进程崩溃时,操作系统会立即回收该进程占用的所有资源,包括已打开的文件描述符。对于 TCP socket,内核会代替进程完成剩下的工作 :自动调用 close,触发标准的 四次挥手 流程。

之所以能正常挥手,是因为 TCP 控制块是内核管理的,并不随进程消失而消失。只要内核还在运行,连接状态机就可以继续运转,完成 FIN 报文的发送与确认。
进程B(正常) 操作系统内核(A侧) 进程A(崩溃) 进程B(正常) 操作系统内核(A侧) 进程A(崩溃) 内核代劳,完成标准四次挥手,连接正常释放 进程崩溃,socket 被回收 FIN (A 主动关闭) ACK FIN (B 主动关闭) ACK

关键点 :进程崩溃与主动调用 exitclose 没有本质区别。只要主机未断电、内核仍在运行,TCP 就能优雅关闭。

正常关机

正常关机时,操作系统会向所有用户进程发送终止信号(如 SIGTERM),进程有机会在退出前调用 close 发出 FIN,尝试完成四次挥手。然而实际情况往往是这样:

  1. A 发出 FIN,B 回复 ACK,A 进入 FIN_WAIT_2 状态,等待 B 的 FIN。
  2. 此时若 A 的系统关机速度较快,在内核完全停止网络子系统之前,B 才发出自己的 FIN。
  3. A 的内核已经无法再处理这个 FIN,也就不会回复最后的 ACK。

B 侧因收不到 ACK,会不断重传 FIN。当重传次数耗尽后,B 判定连接失效,发送 RST 并释放资源。
主机B 主机A(正常关机) 主机B 主机A(正常关机) 进入 FIN_WAIT_2,等待 B 的 FIN A 关机,网络栈停止 无响应(内核已停止处理报文) loop 重传多次 超时,发送 RST 并释放连接 FIN(A发起挥手) ACK FIN(B 也准备关闭) FIN

所以正常关机不一定能完整完成四次挥手,最终往往仍是超时加 RST 收场,除非 B 的 FIN 在 A 关机前就到达并完成确认。

主机突然掉电

突然掉电比正常关机更恶劣,主机没有任何机会发出 FIN 报文。对端 B 完全不知道 A 已经消失,仍认为连接处于 ESTABLISHED 状态。

若 B 向 A 发送数据,这些数据均得不到 ACK,触发 TCP 超时重传机制

  • B 的重传定时器不断超时,采用指数退避策略重传数据。
  • 达到系统设定的重传次数上限(如 tcp_retries2)后,TCP 协议栈认为连接不可达。
  • 此时 B 会发送 RST(复位报文段)(如果网络路径上对方真的可达),并强制关闭本端连接,释放资源。

主机B 主机A(突然掉电) 主机B 主机A(突然掉电) 彻底掉电,无任何应答 每次超时,重传间隔加倍 loop 超时重传,指数退- 避 重传次数耗尽,发送 RST 强制释放本端连接 发送数据(无 ACK) 重传数据 RST

如果 B 在掉电后没有数据要发送,就会一直保持僵死连接,直到触发 保活机制

保活机制(Keep-Alive)

TCP 协议内置了保活(Keep-Alive)机制 。当连接长时间没有数据交互时,系统会周期性地发送空 ACK 报文(心跳探测包)来确认对方是否仍在。

默认参数(各操作系统可调):

  • keepalive_time:连接空闲多久后开始探测,通常 2 小时。
  • keepalive_intvl:两次探测之间的间隔,通常 75 秒。
  • keepalive_probes:最大探测次数,通常 9 次。

流程如下:






连接空闲
超过 keepalive_time?
发送 keepalive 探测包
收到 ACK?
等待 keepalive_intvl
探测次数 < keepalive_probes?
发送 RST, 关闭连接
释放本端资源

若多次探测均无回应,则认为对端不可达,发送 RST 并单方面关闭连接,应用程序会得到"连接超时"或类似的错误。

网线断开

网线断开可以看作是双方同时遭遇对端掉电。断开瞬间,双方都无法立即感知:

  • 站在 A 视角:自己发的数据没有 ACK,B 就像掉电一样。
  • 站在 B 视角:同样的情况。

实际后果取决于是否有数据发送:

  • 有数据发送的一方:通过超时重传最终检测到异常,发送 RST 并关闭连接。
  • 没有数据发送的一方:若无上层心跳,则只能依赖 TCP 保活定时器来发现连接已死(可能等待数小时)。

因此,网线断开的最终结果通常是:

  • 正在通信的一端在几十秒到几分钟内(取决于重传配置)发现故障并释放连接;
  • 空闲的一端可能要很久才能察觉(全靠保活)。
异常场景 对端如何感知 TCP 协议行为 最终结果
进程崩溃 正常收到 FIN 内核代发 FIN,进行四次挥手 优雅释放连接
正常关机 可能收到 FIN,但最后的 ACK 常丢失 A 发 FIN,若 B 的 FIN 未及时被确认则超时 B 超时后发 RST 释放
突然掉电 数据无 ACK,触发超时重传 重传耗尽后发 RST 强制关闭,发送 RST
网线断开 类似双向掉电 有数据一侧通过超时感知,空闲侧靠保活 超时或保活失败后发 RST
长时间无数据 保活探测无响应 经过多次探测后发 RST 单方面释放连接
相关推荐
考虑考虑2 小时前
Mybatis实现批量插入
java·后端·mybatis
咖啡八杯3 小时前
GoF设计模式——中介者模式
java·后端·spring·设计模式
青石路7 小时前
记一次多JDK版本问题的排查,一坑套一坑,差点没爬上来
java
像我这样帅的人丶你还10 小时前
Java 后端详解(五):Redis 缓存
java·后端·全栈
plainGeekDev12 小时前
GreenDAO → Room
android·java·kotlin
亦暖筑序17 小时前
Java 8老系统AI Workflow实战:把一次性AI对话升级成可恢复工作流
java·后端
敲代码的彭于晏17 小时前
Bean 生命周期完全图解:前端同学也能看懂的 Spring 核心机制
java·前端·后端
plainGeekDev18 小时前
ButterKnife → ViewBinding
android·java·kotlin
像我这样帅的人丶你还1 天前
Java 后端详解(四):分页与搜索
java·javascript·后端
她的男孩1 天前
数据权限为什么不能只靠注解?Forge 的 Mapper 层 SQL 改写源码拆解
java·后端·架构