飞翔飞翔
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
  • 学习路径
  • IP协议

    • 认识 IP 协议与网络层定位
    • IPv4 编址体系与分类地址
    • CIDR 与子网划分实战
    • IPv6 编址体系
    • ARP 协议详解
    • NDP 协议详解
    • IGMP 与 MLD 组播侦听发现
    • VRRP 与网关冗余
    • IPv4 数据报首部解析
    • IPv6 数据报与扩展首部
    • IPsec 安全扩展
    • 分片、MTU 与路径发现
    • ICMP 与 Traceroute 原理
    • 路由基础与转发流水线
    • 动态路由协议
    • NAT 网络地址转换
    • DHCP 与自动配置
    • Wireshark 与命令行工具
    • IPv6 过渡技术
    • IP 协议栈排障与攻击防御
  • TCP协议

    • 认识 TCP 协议与传输层定位
    • TCP 报文段首部解析
    • 三次握手与连接建立
    • 四次挥手与连接释放
    • TCP 有限状态机
    • 序列号与确认机制
    • 超时重传与 RTO 计算
    • 滑动窗口与流量控制
    • 拥塞控制基础
    • 现代拥塞控制算法
    • TCP 选项与扩展
    • TCP 性能调优与内核参数
    • Nagle 算法与糊涂窗口综合征
    • TCP 定时器与 Keep-Alive 机制
    • TCP 安全与攻击防御
    • TCP 与上层/下层交互
    • TCP 综合实践与排障

TCP 定时器与 Keep-Alive 机制

TCP 为什么需要定时器

TCP 是面向连接的协议,但网络本身不可靠。定时器是 TCP 处理超时、探测状态、回收资源的核心机制。Linux 内核中,TCP 连接维护多个定时器,协同保障通信正确性。

重传定时器(Retransmit Timer)

已在文档 07 详细讲解,此处总结:

  • 触发条件:发送数据后未在 RTO 内收到 ACK
  • RTO 计算:基于 RTT 采样,Jacobson/Karels 算法
  • 指数退避:每次重传 RTO 翻倍,上限通常 120s
  • 快速重传:3 个重复 ACK 立即重传,不等待 RTO

坚持定时器(Persist Timer)

问题场景

接收方通告窗口 = 0(缓冲区满),发送方停止发送。之后接收方空出缓冲区,发送 ACK 通告新窗口。但如果这个 ACK 丢失:

  • 发送方认为窗口仍为 0,永远等待
  • 接收方认为已通告窗口,永远等待数据
  • 死锁!

解决方案

发送方启动坚持定时器,周期性地发送窗口探测报文(1 字节数据):

定时器行为

  • 首次超时:RTO 后发送第一个探测
  • 后续探测:指数退避(RTO × 2, ×4, ×8...),上限 60s
  • 探测次数:通常 10~15 次后放弃连接
  • 探测内容:1 字节数据(必须被接收方确认,即使窗口为 0)
# Linux 相关参数
sysctl net.ipv4.tcp_retries2      # 放弃连接前的重传次数(默认 15)

保活定时器(Keep-Alive Timer)

为什么需要 Keep-Alive

TCP 连接可能因以下原因"静默死亡":

  • 对端主机崩溃(未发送 FIN)
  • 中间路由器故障
  • 网络分区
  • 对端应用死锁但未关闭连接

发送方可能永远等待,连接资源被占用。

Keep-Alive 机制

空闲一段时间后,发送探测报文(Keep-Alive Probe):

Linux 参数

# 空闲多久开始探测(秒,默认 7200 = 2 小时)
sysctl net.ipv4.tcp_keepalive_time=7200

# 探测间隔(秒,默认 75)
sysctl net.ipv4.tcp_keepalive_intvl=75

# 探测次数(默认 9)
sysctl net.ipv4.tcp_keepalive_probes=9

总超时时间 = keepalive_time + keepalive_intvl × keepalive_probes 默认 = 7200 + 75 × 9 = 7875 秒 ≈ 2.2 小时

应用层替代方案

大多数应用(如 WebSocket、MQTT、数据库连接池)使用应用层心跳:

  • 更灵活(间隔可配置为秒级)
  • 可携带业务状态
  • 避免内核参数全局影响

TIME_WAIT 定时器

已在文档 04 详细讲解,此处总结:

  • 触发:主动关闭方发送最后一个 ACK 后进入 TIME_WAIT
  • 持续时间:2MSL(Linux 默认 60s,MSL=30s)
  • 目的:
    1. 确保最后一个 ACK 被接收(若丢失可重传)
    2. 防止旧连接的延迟报文干扰新连接
  • 副作用:大量短连接导致端口/内存耗尽

延迟 ACK 定时器

已在文档 16 提及,此处补充内核机制:

  • 触发:收到数据后启动 40ms 定时器
  • 行为:
    • 40ms 内无数据要发送 → 发送纯 ACK
    • 40ms 内有数据要发送 → 捎带 ACK
  • 例外:收到乱序数据立即回复 ACK(用于快速重传)
# Linux 快速 ACK 模式(禁用延迟 ACK)
setsockopt(sockfd, IPPROTO_TCP, TCP_QUICKACK, &on, sizeof(on));

SYN-ACK 重传定时器

三次握手中,服务端发送 SYN-ACK 后等待客户端 ACK:

Linux 参数 tcp_synack_retries 控制重传次数(默认 5)。

定时器参数速查表

定时器内核参数默认值作用
重传tcp_retries215放弃连接前的重传次数
坚持(无独立参数)RTO 退避窗口探测间隔
保活tcp_keepalive_time7200s空闲多久开始探测
保活tcp_keepalive_intvl75s探测间隔
保活tcp_keepalive_probes9探测次数
TIME_WAITtcp_fin_timeout60sTIME_WAIT 持续时间
SYN-ACKtcp_synack_retries5SYN-ACK 重传次数
SYNtcp_syn_retries6SYN 重传次数

本篇小结

  • 重传定时器:RTO 动态计算,指数退避,上限 120s
  • 坚持定时器:窗口=0 时周期性探测,防止死锁
  • 保活定时器:空闲 2 小时后探测对端存活,默认 9 次
  • TIME_WAIT 定时器:2MSL 确保 ACK 到达、防止旧报文干扰
  • 延迟 ACK 定时器:40ms 等待捎带,乱序数据立即 ACK
  • SYN-ACK 定时器:指数退避重传,默认 5 次
  • 应用层心跳优于 TCP Keep-Alive:更灵活、间隔更短

动手实践

  1. 查看本机所有 TCP 定时器参数:

    sysctl -a | grep -E "keepalive|retries|timeout|syn"
    
  2. 缩短 Keep-Alive 时间(测试环境):

    sudo sysctl -w net.ipv4.tcp_keepalive_time=60
    sudo sysctl -w net.ipv4.tcp_keepalive_intvl=10
    sudo sysctl -w net.ipv4.tcp_keepalive_probes=3
    # 总超时 = 60 + 10×3 = 90 秒
    
  3. 用 Wireshark 观察 Keep-Alive 探测:

    • 建立 SSH 连接,空闲等待
    • 过滤 tcp.keep_alive 或观察 Seq=NextSeq-1 的报文
  4. 观察窗口探测(Persist Timer):

    • 用 nc 建立连接
    • 接收方停止读取,发送方继续发送直到窗口=0
    • 抓包观察 1 字节的窗口探测报文
  5. 思考:为什么坚持定时器发送 1 字节数据而不是纯 ACK?如果发送纯 ACK,接收方如何响应?

上一页
Nagle 算法与糊涂窗口综合征
下一页
TCP 安全与攻击防御