第23章 TCP的保活定时器

所属:TCP/IP详解 卷1:协议 来源:TCP/IP详解 卷1:协议

本章介绍TCP的保活定时器,包括保活的用途、保活的争议和保活的实现等。


23.1 引言

保活定时器

保活定时器(Keepalive Timer):

  • 也叫存活定时器
  • 检测连接是否还活着
  • 对方是不是还在
  • 长时间没数据的时候用

保活定时器检测连接是否存活。


为什么需要保活

为什么需要保活:

场景:

  • 客户端和服务器建立了连接
  • 客户端突然崩溃了
  • 服务器不知道
  • 服务器还以为连接在
  • 一直等
  • 浪费资源

问题:

  • TCP连接是虚拟的
  • 不发数据就不知道对方还在不在
  • 对方崩溃了,这边不知道
  • 资源浪费

解决:

  • 保活定时器
  • 定期发保活探测
  • 看对方有没有回应
  • 没回应就认为连接断了

保活检测对方是否还活着。


保活的争议

保活的争议:

  • 有人觉得保活好
  • 有人觉得保活不好
  • 有争议

支持的理由:

  • 可以检测死连接
  • 释放资源
  • 防止资源泄漏

反对的理由:

  • 浪费带宽
  • 可能误判(网络暂时不通)
  • 可能在计费网络上花钱
  • TCP本来就不应该管这个
  • 应该应用层自己处理

保活有争议。


本章讨论的内容

本章讨论的内容:

  • 保活的用途
  • 保活的争议
  • 保活的实现
  • 保活的参数
  • 保活的例子

本章介绍保活定时器。


23.2 保活的用途

检测死连接

检测死连接:

  • 对方崩溃了
  • 对方断电了
  • 对方网络断了
  • 连接已经没用了
  • 但这边还不知道
  • 保活可以检测出来

检测对方是不是挂了。


防止连接被断开

防止连接被断开:

  • 有些中间设备(比如NAT、防火墙)
  • 会超时断开空闲的连接
  • 保活可以保持连接活跃
  • 不让中间设备断开

为什么中间设备会断开:

  • 资源有限
  • 空闲连接占资源
  • 超时就断开
  • 保活让连接看起来不空闲

保活防止中间设备断开。


服务器端的作用

服务器端的作用:

  • 服务器有很多连接
  • 有些客户端可能崩溃了
  • 连接还占着
  • 浪费服务器资源
  • 保活可以清理死连接

服务器用保活清理死连接。


23.3 保活的争议

为什么有人反对

为什么有人反对保活:

  1. 浪费带宽

    • 没事就发包
    • 浪费网络资源
    • 尤其是计费网络
  2. 可能误判

    • 网络暂时拥塞
    • 保活探测没收到回应
    • 就断开了
    • 其实连接还能用
  3. TCP的职责

    • 有人觉得TCP不应该管这个
    • 这是应用层的事
    • TCP只管可靠传输
    • 不管连接活不活
  4. 计费问题

    • 按流量计费的网络
    • 保活包也要钱
    • 白白花钱

反对的理由。


为什么有人支持

为什么有人支持保活:

  1. 检测死连接

    • 对方崩溃了能检测到
    • 释放资源
    • 不然一直占着
  2. 防止中间设备断开

    • NAT、防火墙超时
    • 保活能保持连接
  3. 应用层简单

    • 不用应用自己实现
    • TCP提供了
    • 方便

支持的理由。


默认是关闭的

默认是关闭的:

  • 很多实现默认关闭保活
  • 因为有争议
  • 需要应用自己打开
  • 用SO_KEEPALIVE选项

为什么默认关闭:

  • 有争议
  • 不是必须的
  • 让应用自己决定

默认关闭,需要自己打开。


23.4 保活的实现

保活的参数

保活的参数:

  1. 保活时间(Keepalive Time)

    • 连接空闲多久开始发探测
    • 通常是2小时
    • 7200秒
  2. 保活间隔(Keepalive Interval)

    • 每次探测之间的间隔
    • 通常是75秒
  3. 保活次数(Keepalive Probes)

    • 发多少次探测没回应才断开
    • 通常是9次

总时间:

  • 2小时 + 9 × 75秒 = 2小时11分15秒
  • 大约2小时11分钟
  • 很长时间

保活参数。


保活的过程

保活的过程:

  1. 连接空闲

    • 一段时间没有数据
    • 保活时间到了
  2. 发探测包

    • 发一个保活探测
    • 就是一个空的ACK
    • 序号是之前的减1
  3. 收到回应

    • 对方回应了
    • 连接还活着
    • 重置定时器
    • 再等保活时间
  4. 没收到回应

    • 等保活间隔
    • 再发一个探测
    • 重复
  5. 多次没回应

    • 发了N次都没回应
    • 认为连接断了
    • 关闭连接

保活的工作过程。


保活探测包

保活探测包:

  • 一个空的ACK
  • 序号比当前的小1
  • 对方收到会回ACK
  • 因为序号不对
  • 但不影响数据

为什么序号减1:

  • 确保对方会回应
  • 即使没有新数据
  • 也会回ACK

保活探测是序号减1的ACK。


几种情况

保活探测的几种结果:

  1. 对方正常

    • 收到探测
    • 回ACK
    • 连接正常
    • 重置定时器
  2. 对方崩溃了

    • 没回应
    • 多次探测都没回应
    • 超时断开
  3. 对方崩溃后重启了

    • 收到探测
    • 不知道这个连接
    • 回RST
    • 连接复位
  4. 网络不通

    • 探测包过不去
    • 回应也回不来
    • 多次探测没回应
    • 超时断开

四种情况。


23.5 保活和应用层心跳

应用层心跳

应用层心跳(Application Heartbeat):

  • 应用自己实现的保活
  • 应用定期发心跳包
  • 检测对方是否存活
  • 比TCP保活更灵活

应用层也可以做心跳。


TCP保活 vs 应用层心跳

TCP保活 vs 应用层心跳:

对比项TCP保活应用层心跳
实现位置TCP层应用层
复杂度简单,打开就行复杂,自己实现
灵活性低,参数固定高,自己控制
功能只能检测连接可以检测应用状态
开销小大一些
跨平台可能有差异自己控制,一致

各有优缺点。


什么时候用哪个

什么时候用TCP保活:

  • 简单需求
  • 只要检测连接
  • 不想自己实现
  • 快速实现

什么时候用应用层心跳:

  • 需要更灵活
  • 需要检测应用状态
  • 参数要自定义
  • 跨平台一致性

根据需求选择。


23.6 小结

保活定时器概述

  1. 什么是保活定时器

    • 检测连接是否存活
    • 长时间没数据时用
  2. 为什么需要

    • 对方崩溃了不知道
    • 浪费资源
    • 保活可以检测
  3. 争议

    • 有人觉得好
    • 有人觉得不好
    • 默认关闭

保活的用途

  1. 检测死连接

    • 对方崩溃、断电、断网
    • 检测出来释放资源
  2. 防止中间设备断开

    • NAT、防火墙超时
    • 保活保持连接活跃
  3. 服务器端

    • 清理死连接
    • 释放资源

保活的争议

  1. 反对的理由

    • 浪费带宽
    • 可能误判
    • 不是TCP的职责
    • 计费问题
  2. 支持的理由

    • 检测死连接
    • 防止中间设备断开
    • 应用层简单
  3. 默认关闭

    • 有争议
    • 需要自己打开
    • SO_KEEPALIVE

保活的实现

  1. 参数

    • 保活时间:通常2小时
    • 保活间隔:通常75秒
    • 保活次数:通常9次
  2. 过程

    • 空闲到时间发探测
    • 收到回应重置
    • 没回应继续发
    • 多次没回应断开
  3. 探测包

    • 空的ACK
    • 序号减1
  4. 四种结果

    • 对方正常:回ACK
    • 对方崩溃:没回应
    • 对方重启:回RST
    • 网络不通:没回应

应用层心跳

  1. 什么是应用层心跳

    • 应用自己实现
    • 更灵活
  2. vs TCP保活

    • TCP保活简单
    • 应用层灵活
  3. 选择

    • 简单需求用TCP保活
    • 复杂需求用应用层心跳

关键概念

  1. 保活定时器

    • 检测连接存活
    • 有争议
  2. 保活参数

    • 时间、间隔、次数
    • 通常2小时开始
  3. 保活探测

    • 序号减1的ACK
    • 对方必须回应
  4. 应用层心跳

    • 应用自己实现
    • 更灵活

TCP keepalive