第24章 TCP:保活定时器

所属:TCP/IP详解 卷2:实现 来源:TCP/IP详解 卷2:实现

本章描述TCP保活定时器的实现,包括保活的作用、工作原理、争议和实现细节。


24.1 引言

保活定时器的作用

保活定时器(Keepalive Timer):

  • 探查空闲连接是否还活着
  • 长时间没有数据的连接
  • 定期发送探查
  • 确认对端是否还在

保活定时器探查空闲连接的状态。


为什么需要保活

为什么需要保活:

场景:

  1. 客户端和服务端建立了连接
  2. 客户端突然崩溃了(断电、死机等)
  3. 没有发FIN
  4. 服务端不知道客户端挂了
  5. 连接一直挂着
  6. 浪费资源

保活的作用:

  • 定期探查
  • 发现对端不在了
  • 释放资源

保活可以发现死连接。


保活的争议

保活的争议:

反对的理由:

  1. 可能误判

    • 网络临时不通
    • 对端只是暂时没响应
    • 不是真的挂了
    • 断开了很可惜
  2. 浪费带宽

    • 空闲连接也要发报文
    • 很多连接的话,流量不小
    • 增加网络负担
  3. 不是标准要求

    • TCP标准没有要求
    • 是可选功能
    • 不应该默认开启
  4. 应用层应该自己处理

    • 应用自己做心跳
    • 更灵活
    • 不应该在TCP层做

支持的理由:

  1. 可以及时发现死连接

    • 释放资源
    • 避免资源泄漏
  2. 很多应用需要

    • 服务器有很多连接
    • 死连接占资源
    • 需要清理

结论:

  • 可选功能
  • 默认关闭
  • 需要时开启
  • 谨慎使用

保活有争议,是可选功能。


本章讨论的内容

本章讨论的内容:

  • 保活的工作原理
  • 保活的时间线
  • 保活的实现
  • 保活的争议
  • 保活的使用场景

本章讨论保活定时器的实现。


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分钟

空闲很久才开始探查,频率很低。


探查的结果

保活探查的可能结果:

  1. 收到应答

    • 对端还活着
    • 重置保活定时器
    • 再等2小时
  2. 收到RST

    • 对端已经重启了
    • 连接不存在了
    • 关闭连接
  3. 没收到应答

    • 可能丢了
    • 继续发下一个
    • 发了10个都没回
    • 认为连接断了
    • 关闭连接

不同结果不同处理。


24.3 保活的实现

保活定时器

保活定时器:

  • tcpcb中的t_timer[TCPT_KEEP]
  • 单位是500ms的ticks
  • 0表示没运行

什么时候启动:

  • 连接建立后
  • 有数据收发就重置
  • 空闲时间到了就开始探查

和其他定时器一起管理。


保活的状态

保活的状态:

  1. 空闲等待

    • 等2小时
    • 有数据就重置
  2. 探查阶段

    • 每隔75秒发一个
    • 发10个
    • 都没回就断开

两个阶段:空闲等待和探查。


保活的触发

保活探查的触发:

  • 保活定时器超时
  • 发送探查报文
  • 设置下一次超时
  • 计数加1

收到应答:

  • 重置计数器
  • 回到空闲等待
  • 再等2小时

超时就发探查,收到应答就重置。


24.4 保活的使用场景

什么时候用保活

适合用保活的场景:

  1. 服务器

    • 很多客户端连接
    • 客户端可能崩溃
    • 死连接占资源
    • 需要清理
  2. 长连接

    • 连接时间很长
    • 中间可能断
    • 需要知道对端还在不在
  3. 检测对等方崩溃

    • 对端崩溃了没发FIN
    • 保活可以发现

服务器和长连接适合用。


什么时候不用保活

不适合用保活的场景:

  1. 短连接

    • 连接时间短
    • 用完就关
    • 不需要保活
  2. 对实时性要求不高

    • 不在乎死连接
    • 资源够用
    • 不用也行
  3. 应用层有心跳

    • 应用自己做了
    • 不用TCP层的

不是所有场景都需要。


应用层心跳 vs TCP保活

应用层心跳 vs TCP保活:

对比项TCP保活应用层心跳
实现TCP层自动应用自己实现
灵活性低,参数固定高,自己控制
开销小,空报文大,有应用数据
功能只探活可以带其他信息
跨平台可能有差异自己控制,一致

建议:

  • 简单探活用TCP保活
  • 需要更多功能用应用层心跳

各有优缺点,按需选择。


24.5 保活的参数

可配置的参数

保活的可配置参数:

  1. 空闲时间

    • 多久没数据开始探查
    • 默认2小时
    • 可以改
  2. 探查间隔

    • 每次探查之间的间隔
    • 默认75秒
    • 可以改
  3. 探查次数

    • 发多少次没回就断开
    • 默认10次
    • 可以改

参数可以配置。


怎么开启保活

怎么开启保活:

  • setsockopt设置SO_KEEPALIVE选项
  • 应用程序设置
  • 默认关闭

例子:

int keepalive = 1;
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive));

应用设置SO_KEEPALIVE开启。


24.6 小结

保活概述

  1. 作用

    • 探查空闲连接
    • 发现死连接
    • 释放资源
  2. 争议

    • 可能误判
    • 浪费带宽
    • 不是标准要求
    • 可选功能

工作原理

  1. 基本原理

    • 空闲一段时间
    • 定期探查
    • 没应答就断开
  2. 探查报文

    • 空ACK
    • 序号减1
    • 强制对端回复
  3. 时间线

    • 空闲2小时
    • 每75秒一个探查
    • 10次没回就断开
    • 总共约2小时12分

实现

  1. 定时器

    • t_timer[TCPT_KEEP]
    • 500ms单位
  2. 状态

    • 空闲等待
    • 探查阶段
  3. 触发

    • 超时发探查
    • 收到应答重置

使用场景

  1. 适合的场景

    • 服务器
    • 长连接
    • 检测崩溃
  2. 不适合的场景

    • 短连接
    • 要求不高
    • 应用有心跳
  3. vs 应用层心跳

    • TCP保活:简单,开销小
    • 应用心跳:灵活,功能强

参数

  1. 可配置参数

    • 空闲时间
    • 探查间隔
    • 探查次数
  2. 开启方式

    • SO_KEEPALIVE选项
    • setsockopt设置

关键概念

  1. 保活定时器

    • 探查空闲连接
    • 发现死连接
  2. 保活探查

    • 空ACK
    • 序号减1
  3. 争议

    • 有争议
    • 可选功能
    • 谨慎使用
  4. 使用场景

    • 服务器
    • 长连接
    • 按需使用

keepalive 保活