第23章 TCP:坚持定时器

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

本章描述TCP坚持定时器的实现,包括坚持定时器的作用、工作原理和糊涂窗口综合征的避免等。


23.1 引言

坚持定时器的作用

坚持定时器(Persist Timer):

  • 防止零窗口死锁
  • 窗口为0时启动
  • 周期性探查窗口是否恢复
  • 也叫窗口探查定时器

坚持定时器防止零窗口死锁。


什么是零窗口死锁

零窗口死锁(Zero Window Deadlock):

场景:

  1. 接收方缓冲区满了,通告窗口为0
  2. 发送方收到,停止发送
  3. 接收方缓冲区有空间了,发送窗口更新(ACK)
  4. 这个ACK丢了
  5. 发送方等接收方发窗口更新
  6. 接收方等发送方发数据
  7. 两边都等,死锁了

为什么会死锁:

  • ACK是不可靠的
  • ACK丢了不会重传
  • 发送方不知道窗口恢复了
  • 接收方以为发送方收到了

零窗口可能导致死锁。


本章讨论的内容

本章讨论的内容:

  • 零窗口问题
  • 坚持定时器的工作原理
  • 窗口探查
  • 糊涂窗口综合征
  • 坚持定时器的实现

本章讨论坚持定时器的实现。


23.2 坚持定时器的工作原理

什么时候启动

启动坚持定时器的时机:

  • 发送方要发送数据
  • 但对端通告的窗口为0
  • 不能发送
  • 启动坚持定时器

什么时候停止:

  • 收到非零窗口的ACK
  • 窗口恢复了
  • 可以发送了
  • 停止定时器

窗口为0就启动,窗口恢复就停止。


坚持定时器的超时时间

超时时间:

  • 初始值比较短
  • 然后指数退避
  • 类似重传定时器

典型值:

  • 第一次:5秒
  • 第二次:10秒
  • 第三次:20秒
  • …
  • 最大:60秒

为什么指数退避:

  • 防止频繁探查
  • 减少网络流量
  • 给对端恢复的时间

超时时间指数退避。


窗口探查

窗口探查(Window Probe):

  • 坚持定时器超时
  • 发送一个字节的数据
  • 这个字节是窗口外的
  • 但可以发,用来试探

探查的特点:

  • 只发1字节
  • 即使窗口为0也能发
  • 对端收到后必须回复ACK
  • ACK中包含新的窗口大小

发1字节探查窗口。


窗口探查的结果

窗口探查的可能结果:

  1. 窗口还是0

    • 对端回复ACK,窗口还是0
    • 重新启动坚持定时器
    • 超时时间更长(指数退避)
  2. 窗口恢复了

    • 对端回复ACK,窗口非0
    • 停止坚持定时器
    • 可以发送数据了
  3. 探查丢了

    • 没收到ACK
    • 定时器超时
    • 再发一个探查
    • 超时时间更长

探查后根据结果处理。


23.3 糊涂窗口综合征

什么是糊涂窗口综合征

糊涂窗口综合征(Silly Window Syndrome, SWS):

  • 窗口很小就发送
  • 每个段只有很少的数据
  • 效率很低
  • 40字节首部 + 1字节数据
  • 开销太大

为什么叫糊涂:

  • 窗口很小,很傻
  • 效率很低
  • 不划算

糊涂窗口综合征是小窗口导致的效率问题。


糊涂窗口综合征的原因

原因:

  1. 接收方的问题

    • 缓冲区有一点空间就通告
    • 窗口很小
    • 发送方就发小的段
  2. 发送方的问题

    • 有一点数据就发
    • 不等数据多一点
    • 发很多小报文

接收方和发送方都可能导致SWS。


接收方的避免

接收方怎么避免:

  • 窗口小就不通告
  • 等窗口大一点再通告
  • 至少达到一定大小才通告

规则:

  • 窗口小于一个MSS就不通告窗口增加
  • 或者小于缓冲区的一半
  • 等够了再通告

接收方小窗口不通告。


发送方的避免

发送方怎么避免:

  • 数据少就不发
  • 等数据多一点再发
  • Nagle算法就是干这个的

Nagle算法:

  • 只有一个未确认的小报文
  • 等ACK回来再发下一个
  • 或者等数据够一个MSS

发送方用Nagle算法避免。


两边都要避免

两边都要避免:

  • 接收方:小窗口不通告
  • 发送方:小数据不发送
  • 两边一起防止SWS

为什么两边都要:

  • 光一边不够
  • 两边都防止才保险
  • 提高效率

双方共同防止糊涂窗口综合征。


23.4 坚持定时器的实现

定时器的管理

坚持定时器的管理:

  • 每个连接一个
  • tcpcb中的t_timer[TCPT_PERSIST]
  • 单位是500ms的ticks
  • 0表示没运行

和其他定时器一起管理。


启动和停止

启动:

  • 要发送数据但窗口为0
  • 设置定时器初值
  • 开始计时

停止:

  • 收到非零窗口的ACK
  • 定时器设为0
  • 停止计时

窗口为0启动,窗口恢复停止。


超时处理

超时处理:

  1. 发送窗口探查(1字节)
  2. 计算下一次的超时时间
  3. 指数退避
  4. 重新启动定时器

探查的数据:

  • 发送窗口外的1字节
  • 就是下一个要发的字节
  • 即使窗口为0也能发

超时就发探查,然后指数退避。


23.5 坚持定时器和重传定时器的区别

两个定时器的区别

坚持定时器 vs 重传定时器:

对比项坚持定时器重传定时器
作用窗口探查超时重传
触发条件窗口为0有未确认数据
超时动作发1字节探查重传数据
目的防止零窗口死锁保证可靠交付
数据1字节探查重传未确认的

两个定时器作用不同。


会不会同时运行

会不会同时运行:

  • 可能
  • 窗口为0,但还有未确认的数据
  • 两个定时器都在跑

优先级:

  • 各跑各的
  • 互不影响
  • 各自超时各自处理

两个定时器可以同时运行。


23.6 小结

坚持定时器概述

  1. 作用

    • 防止零窗口死锁
    • 窗口探查
  2. 为什么需要

    • 零窗口可能死锁
    • ACK丢了
    • 两边都等

工作原理

  1. 启动停止

    • 窗口为0启动
    • 窗口恢复停止
  2. 超时时间

    • 指数退避
    • 初始短,越来越长
  3. 窗口探查

    • 发1字节
    • 试探窗口
    • 收到ACK看窗口大小

糊涂窗口综合征

  1. 什么是SWS

    • 小窗口
    • 效率低
    • 40字节首部+1字节数据
  2. 原因

    • 接收方小窗口通告
    • 发送方小数据发送
  3. 避免

    • 接收方:小窗口不通告
    • 发送方:Nagle算法
    • 两边都防止

实现

  1. 定时器管理

    • tcpcb中的字段
    • 500ms单位
    • 和其他定时器一起
  2. 启动停止

    • 窗口为0启动
    • 窗口恢复停止
  3. 超时处理

    • 发1字节探查
    • 指数退避
    • 重启定时器

和重传定时器的区别

  1. 作用不同

    • 坚持:窗口探查
    • 重传:超时重传
  2. 可以同时运行

    • 互不影响
    • 各自处理

关键概念

  1. 坚持定时器

    • 防止零窗口死锁
    • 窗口探查
  2. 零窗口死锁

    • ACK丢了
    • 两边都等
  3. 糊涂窗口综合征

    • 小窗口效率低
    • 两边都要避免
  4. 窗口探查

    • 发1字节
    • 试探窗口

窗口探查 零窗口