第24章 TCP:保活定时器
所属:TCP/IP详解 卷2:实现 来源:TCP/IP详解 卷2:实现
本章描述TCP保活定时器的实现,包括保活的作用、工作原理、争议和实现细节。
24.1 引言
保活定时器的作用
保活定时器(Keepalive Timer):
- 探查空闲连接是否还活着
- 长时间没有数据的连接
- 定期发送探查
- 确认对端是否还在
保活定时器探查空闲连接的状态。
为什么需要保活
为什么需要保活:
场景:
- 客户端和服务端建立了连接
- 客户端突然崩溃了(断电、死机等)
- 没有发FIN
- 服务端不知道客户端挂了
- 连接一直挂着
- 浪费资源
保活的作用:
- 定期探查
- 发现对端不在了
- 释放资源
保活可以发现死连接。
保活的争议
保活的争议:
反对的理由:
-
可能误判
- 网络临时不通
- 对端只是暂时没响应
- 不是真的挂了
- 断开了很可惜
-
浪费带宽
- 空闲连接也要发报文
- 很多连接的话,流量不小
- 增加网络负担
-
不是标准要求
- TCP标准没有要求
- 是可选功能
- 不应该默认开启
-
应用层应该自己处理
- 应用自己做心跳
- 更灵活
- 不应该在TCP层做
支持的理由:
-
可以及时发现死连接
- 释放资源
- 避免资源泄漏
-
很多应用需要
- 服务器有很多连接
- 死连接占资源
- 需要清理
结论:
- 可选功能
- 默认关闭
- 需要时开启
- 谨慎使用
保活有争议,是可选功能。
本章讨论的内容
本章讨论的内容:
- 保活的工作原理
- 保活的时间线
- 保活的实现
- 保活的争议
- 保活的使用场景
本章讨论保活定时器的实现。
24.2 保活的工作原理
基本原理
保活的基本原理:
- 连接空闲一段时间
- 发送保活探查报文
- 收到应答就重置定时器
- 几次没应答就认为连接断了
- 关闭连接
定期探查,没应答就断开。
保活探查报文
保活探查报文:
- 空的ACK报文
- 序号是当前序号减1
- 这样对端必须回复ACK
- 因为序号不对
为什么用序号减1:
- 正常的ACK对端可能不回复
- 序号不对,对端必须回复
- 这样就能探活
用序号减1的ACK探查。
保活的时间线
保活的典型时间线:
连接空闲
|
| 2小时
v
开始保活探查
|
| 75秒
v
发第1个探查 → 收到应答 → 重置,再等2小时
|
| 没收到
| 75秒
v
发第2个探查 → 收到应答 → 重置
|
| 没收到
| ...
v
发第10个探查 → 没收到
|
v
认为连接断开
关闭连接
典型参数(4.4BSD):
- 空闲时间:2小时(7200秒)
- 探查间隔:75秒
- 探查次数:10次
- 总时间:2小时 + 10×75秒 = 2小时12.5分钟
空闲很久才开始探查,频率很低。
探查的结果
保活探查的可能结果:
-
收到应答
- 对端还活着
- 重置保活定时器
- 再等2小时
-
收到RST
- 对端已经重启了
- 连接不存在了
- 关闭连接
-
没收到应答
- 可能丢了
- 继续发下一个
- 发了10个都没回
- 认为连接断了
- 关闭连接
不同结果不同处理。
24.3 保活的实现
保活定时器
保活定时器:
- tcpcb中的t_timer[TCPT_KEEP]
- 单位是500ms的ticks
- 0表示没运行
什么时候启动:
- 连接建立后
- 有数据收发就重置
- 空闲时间到了就开始探查
和其他定时器一起管理。
保活的状态
保活的状态:
-
空闲等待
- 等2小时
- 有数据就重置
-
探查阶段
- 每隔75秒发一个
- 发10个
- 都没回就断开
两个阶段:空闲等待和探查。
保活的触发
保活探查的触发:
- 保活定时器超时
- 发送探查报文
- 设置下一次超时
- 计数加1
收到应答:
- 重置计数器
- 回到空闲等待
- 再等2小时
超时就发探查,收到应答就重置。
24.4 保活的使用场景
什么时候用保活
适合用保活的场景:
-
服务器
- 很多客户端连接
- 客户端可能崩溃
- 死连接占资源
- 需要清理
-
长连接
- 连接时间很长
- 中间可能断
- 需要知道对端还在不在
-
检测对等方崩溃
- 对端崩溃了没发FIN
- 保活可以发现
服务器和长连接适合用。
什么时候不用保活
不适合用保活的场景:
-
短连接
- 连接时间短
- 用完就关
- 不需要保活
-
对实时性要求不高
- 不在乎死连接
- 资源够用
- 不用也行
-
应用层有心跳
- 应用自己做了
- 不用TCP层的
不是所有场景都需要。
应用层心跳 vs TCP保活
应用层心跳 vs TCP保活:
| 对比项 | TCP保活 | 应用层心跳 |
|---|---|---|
| 实现 | TCP层自动 | 应用自己实现 |
| 灵活性 | 低,参数固定 | 高,自己控制 |
| 开销 | 小,空报文 | 大,有应用数据 |
| 功能 | 只探活 | 可以带其他信息 |
| 跨平台 | 可能有差异 | 自己控制,一致 |
建议:
- 简单探活用TCP保活
- 需要更多功能用应用层心跳
各有优缺点,按需选择。
24.5 保活的参数
可配置的参数
保活的可配置参数:
-
空闲时间
- 多久没数据开始探查
- 默认2小时
- 可以改
-
探查间隔
- 每次探查之间的间隔
- 默认75秒
- 可以改
-
探查次数
- 发多少次没回就断开
- 默认10次
- 可以改
参数可以配置。
怎么开启保活
怎么开启保活:
- setsockopt设置SO_KEEPALIVE选项
- 应用程序设置
- 默认关闭
例子:
int keepalive = 1;
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive));应用设置SO_KEEPALIVE开启。
24.6 小结
保活概述
-
作用
- 探查空闲连接
- 发现死连接
- 释放资源
-
争议
- 可能误判
- 浪费带宽
- 不是标准要求
- 可选功能
工作原理
-
基本原理
- 空闲一段时间
- 定期探查
- 没应答就断开
-
探查报文
- 空ACK
- 序号减1
- 强制对端回复
-
时间线
- 空闲2小时
- 每75秒一个探查
- 10次没回就断开
- 总共约2小时12分
实现
-
定时器
- t_timer[TCPT_KEEP]
- 500ms单位
-
状态
- 空闲等待
- 探查阶段
-
触发
- 超时发探查
- 收到应答重置
使用场景
-
适合的场景
- 服务器
- 长连接
- 检测崩溃
-
不适合的场景
- 短连接
- 要求不高
- 应用有心跳
-
vs 应用层心跳
- TCP保活:简单,开销小
- 应用心跳:灵活,功能强
参数
-
可配置参数
- 空闲时间
- 探查间隔
- 探查次数
-
开启方式
- SO_KEEPALIVE选项
- setsockopt设置
关键概念
-
保活定时器
- 探查空闲连接
- 发现死连接
-
保活探查
- 空ACK
- 序号减1
-
争议
- 有争议
- 可选功能
- 谨慎使用
-
使用场景
- 服务器
- 长连接
- 按需使用