TCP 重传导致工业相机通信中断的原因与排查

· 更新日期: · · TCP, 网络, 故障排查, Windows开发, 工业相机

更新记录(3 条,最后更新 2026年09月03日)

本文的修改记录。已保存的更新前版本,可通过带有 DOI 的永久链接阅读。

本文此前是日文原文的节译,缺少大量章节、表格、Mermaid 图、图题、脚注与 FAQ。现已改写为日文原文的完整译文,技术主张与日文版一致,并补上了此前缺失的图与表,同时统一了全篇的术语译法。 查看更新前的版本 (DOI: 10.5281/zenodo.22276579)
补充了日文原文中已有的咨询引导(consultation_services)。正文内容没有改动。
修复了参考链接等处含有竖线(管道)符号的行被渲染为表格、导致链接无法点击的显示错误。正文内容未作改动。
首次发布
引用本文(DOI: 10.5281/zenodo.21615432)

本文保存于 Zenodo。以下同时提供始终指向最新版本的 DOI,以及固定于您正在阅读版本的 DOI。

小村 豪(2026)。《TCP 重传导致工业相机通信中断的原因与排查》。小村软件有限公司。https://doi.org/10.5281/zenodo.21615432 https://comcomponent.com/zh-CN/blog/2026/03/11/001-tcp-retransmission-rfc1323-industrial-camera/

DOI(最新版本)
10.5281/zenodo.21615432
DOI(此版本)
10.5281/zenodo.22281941

在工业相机或设备控制的通信中,最麻烦的现象就是 平均速度很快,却偶尔停顿几秒。复现率很低,平时又什么都不发生,于是 UI、线程、GC、相机 SDK、NIC、交换机,全都显得有点可疑。

本文要讨论的,是控制工业相机的应用与主机之间的 TCP 通信偶尔会停顿数秒这一现象。调查下来发现,真正的原因并不是应用停止,而是 丢包引发的 TCP 重传等待。进一步地,启用 RFC1323 系列的时间戳功能(按当前的规范整理对应 RFC 7323)之后,在这套系统上把等待时间压到了最低限度。

文中的设备名称、架构和数值都做了一般化处理,但思路本身可以直接用于实务。

目录

  1. 先说结论(一句话)
  2. 症状的表现
    • 2.1. 应用还活着,只有响应停顿数秒
    • 2.2. 发生频率低,只看日志很难察觉
  3. 到底发生了什么(图)
    • 3.1. 从丢包进入重传等待
    • 3.2. 秒级停顿与 RTO 的形态相吻合
  4. 排查时关注的要点
    • 4.1. 先排除应用内部的停顿因素
    • 4.2. 用抓包确认重传
    • 4.3. 查看协商出来的 TCP 选项
  5. RFC1323 时间戳为什么有效
    • 5.1. 时间戳是为了 RTTM 与 PAWS
    • 5.2. 可以消除重传时 RTT 测量的模糊性
    • 5.3. 本案例中能压缩等待时间的原因
  6. 实际采取的对策
    • 6.1. 启用时间戳
    • 6.2. 在 SYN / SYN-ACK 中确认 TSopt
    • 6.3. 这样做仍然无效时该看哪里
  7. 在 Wireshark 中要看的要点
  8. 大致的判断分工
  9. 总结
  10. 参考资料

图中实线表示始终成立的关系,虚线表示带条件的关系(成立条件写在详情页各关系的说明中)。关系的完整列表(共 19 条,附依据与确信度)以及主要概念的定义,汇总在知识地图详情页(日文)。数据:JSON-LD / Turtle

1. 先说结论(一句话)

  • 偶尔停顿数秒的 TCP 通信,真正的原因有时并不是应用停止,而是 丢包之后的重传等待
  • 如果抓包里能看到 Retransmission 和明显的时间差,而且停顿时长与 RTO 的等待方式对得上,那嫌疑就相当大
  • TCP timestamps option 是为 RTT 测量和 PAWS 而设计的机制,同时也能消除重传时 RTT 测量的模糊性
  • 在本案例中,启用 RFC1323 系列的时间戳功能之后,减少了 RTO 估计一直停留在陈旧而保守的状态的时间,把秒级停顿压到了最低限度
  • 不过,这并不是 能消除丢包本身的魔法。物理层、NIC、交换机、中间设备、驱动、缓冲区设计的复查,仍然要另外做

一句话说,如果“偶尔停顿几秒”的真身是 TCP 内部的等待时间,那么只在应用层的重试上使劲就打不到要害。先看 wire(线路上的实际数据包),把是不是重传等待确定下来,反而更快。

本文结论的脉络偶尔停顿数秒的TCP通信应先看wire确定是不是重传等待,如果是重传等待则timestamps有可能压缩等待时间,但丢包本身的调查仍需另外进行。偶尔停顿数秒的TCP通信先看wire确定是不是重传等待timestamps有可能压缩等待丢包源的调查要另外做

图1:在应用层重试上使劲之前,先用 wire 判断“究竟卡在哪里等待”。

后面会连续出现 TCP 的缩写。这里先为不专攻网络的 Windows 开发者整理一份对照。只要记住这些,后面读起来就不会卡壳。

术语 全称 含义
ACK Acknowledgment 确认应答。用来告诉对方“我已经收到这里为止”的机制
RTT Round-Trip Time 往返时延。从发出去到收到 ACK 为止的时间
RTO Retransmission Timeout 重传计时器。在这段时间内收不到 ACK 就重传。由 RTT 的估计值计算得出
RTTM Round-Trip Time Measurement RTT 的测量。这是时间戳功能的目的之一
PAWS Protect Against Wrapped Sequences 针对序列号回绕这类问题的防护。这是时间戳功能的另一个目的
TSopt TCP Timestamps Option TCP 首部中的一个选项。类型为 8,长度 10 字节,携带下面这两个值
TSval Timestamp Value 发送方填入的、自己这一侧时钟的值
TSecr Timestamp Echo Reply 把收到的 TSval 原样返回的字段。凭这个就能知道“这是对哪一次发送的 ACK”
SACK Selective Acknowledgment 接收方逐段告知“自己收到了哪些范围”的机制
Dup ACK Duplicate ACK 指向同一序列号的 ACK 重复返回。这是中间有缺口的信号
fast retransmit — Dup ACK 累积到一定数量后,不等 RTO 超时就重传的机制。Windows 的默认行为是“收到指向同一序列号的 3 个 ACK(最初 1 个加上重复的 2 个)就触发”
Karn 算法 — 规定不得从重传过的分段上采集 RTT 样本。原因是无法判断那是对哪一次发送的 ACK

2. 症状的表现

2.1. 应用还活着,只有响应停顿数秒

一开始最让人困惑的是,整个应用看起来并没有卡死。

  • UI 没有完全失去响应
  • 进程也没有崩溃
  • CPU 也没有被占满
  • 但相机控制命令的响应 偶尔 会消失数秒

这类症状与应用内部的 deadlock 或死循环也很难区分。而且在设备控制场景中,一次数秒的停顿就会直接给人产线停止的印象。即便平均值很漂亮,现场的实际体感也会差很多。

2.2. 发生频率低,只看日志很难察觉

这类故障麻烦的地方在于发生频率低。表现出来大概是一小时一次、半天一次,或者只在多个条件叠加时才出现。

如果只靠日志追查,大致会变成这样。

  • 应用日志上停在“已发送”“没有返回”
  • 接收方日志看起来是“什么都没收到”
  • 恰好同一时间段还发生了别的事件,嫌疑对象因此散开

这种时候,如果只想用应用日志还原因果关系,很容易陷进泥潭。往下走一层到通信层反而更快。

只看日志定不了元凶的结构应用日志停在已发送但没有返回,接收方日志看起来什么都没收到,同一时间段的其他事件又让嫌疑对象散开,因此不要只靠应用日志还原因果,往下走到通信层更快。应用日志:已发送但没有返回嫌疑散开陷入泥潭接收方日志:什么都没收到同一时间段的其他事件往下走一层到通信层

图2:低频停顿只靠日志追查会陷入泥潭。看数据包更快。

3. 到底发生了什么(图)

3.1. 从丢包进入重传等待

这次的脉络很简单。数据包在传输途中的某处丢失,发送方等待 ACK,一直等不到,于是等 RTO 超时后重传。

相机侧网络主机应用相机侧网络主机应用在这里丢包收不到 ACK 所以等待这次请求的恢复中夹着 RTO 等待通信在这里恢复控制命令 (Seq=N)重传控制命令重传数据包到达ACKACK

图3:数据包丢失后收不到 ACK,要等 RTO 超时才重传。这段时间在应用看来就是“数秒的停顿”。

从应用的角度看这像是“停顿了数秒”,但从 TCP 的角度说,只是“还没收到 ACK,所以在等重传计时器超时”而已。虽然不起眼,但这种停顿方式其实很常见。

这次的控制通信以小型 request/response 居多,一次交互中并没有大量未被 ACK 确认的数据在飞。因此在收集到足够的 duplicate ACK 去触发 fast retransmit 之前,RTO 等待 更容易先暴露出来,这就是这套系统的构成特点。

控制通信中RTO等待容易暴露的原因以小型request/response为主的控制通信中未被ACK确认的数据很少,duplicate ACK收集不够因而难以触发fast retransmit,结果RTO等待更容易暴露出来。以小型request/response为主未被ACK确认的数据少Dup ACK收集不够难以触发fast retransmitRTO等待暴露出来

图4:与大量数据流动的通信不同,控制通信的丢包容易变成等 RTO 超时。

3.2. 秒级停顿与 RTO 的形态相吻合

TCP 的重传等待虽然各实现之间略有差异,但整体上都采用保守的等待方式。RFC 6298 规定,初始 RTO 以 1 秒为基准,计算结果小于 1 秒就向上取整到 1 秒,一旦发生超时就翻倍。

是否丢包收不到 ACKRTO 等待重传是否收到 ACK?通信恢复RTO 翻倍

图5:每次收不到 ACK,RTO 就翻倍。1 秒、2 秒、4 秒这样的等待方式就来自这个形态。

所以即便是本该在几百毫秒内结束的场景,条件不利时也可能呈现 1 秒、2 秒、4 秒这样的等待。这次的“偶尔停顿数秒”,与这个形态吻合得相当自然。

4. 排查时关注的要点

4.1. 先排除应用内部的停顿因素

没有一上来就咬定是 TCP,而是先排除了应用侧的典型因素。

确认对象 排查理由 本次结论
UI 线程 / 工作线程 确认是否挂起或相互等待 不是主因
CPU 使用率 确认是否因高负载导致处理延迟 停顿时也没有被占满
GC / 内存压力 确认是否存在暂停 停顿时长的形态对不上
相机 SDK 调用 确认 SDK 内部是否有等待 与 wire 上的延迟不一致
抓包 确认通信层的重传 在这里看清了原因的脉络

这里重要的是,不要只凭应用日志的时刻就判定元凶。在设备控制类应用中,上层的等待往往只是下层等待的一个投影。

先排除应用内因素的排查顺序不要一上来就咬定TCP,先排除线程、CPU、GC、相机SDK这些应用内的典型停顿因素,再用抓包确认通信层的重传,这就是排查的顺序。先排除线程、CPU、GC、SDK用抓包看通信层重传的脉络显现出来不要只凭日志时刻判定元凶

图6:不要一上来就下结论。先排除应用内的典型因素,再往下走到 wire。

4.2. 用抓包确认重传

抓包之后可以看到,在停顿的时间段里出现了 TCP Retransmission,而且能确认此前 ACK 一直没有返回。

要看的大致是这几点。

  • 是否出现了同一个 Seq 的重传
  • 到重传为止的时间差是否与停顿时长一致
  • 看起来是不是在等 RTO 超时,而不是 Dup ACK 或 Fast Retransmission
  • 出问题的连接是否每次都落在同一个 tcp.stream 上

这些都对得上的话,“不是应用停止,而是 TCP 在等重传”这个判断就会浓厚很多。

确定重传等待的确认点是否出现同一个Seq的重传、时间差是否与停顿时长一致、看起来是不是在等RTO超时,这三点都对上时,就更倾向于是TCP在等重传而不是应用停止。是否出现同一个Seq的重传TCP在等重传时间差是否与停顿时长一致看起来是不是在等RTO超时并不是应用停止了

图7:这 3 点咬合上,“TCP 的重传等待”就比“应用停止”浓厚得多。

4.3. 查看协商出来的 TCP 选项

接下来看的是连接建立时的 SYN / SYN-ACK。时间戳是在 TCP 连接的 3-way handshake 中协商的,所以这里如果没有出现 TSopt,那条连接就不会使用它。

相机侧主机相机侧主机只有在这里协商成功,之后的分段才能用上 TSoptSYN + TSopt ?SYN/ACK + TSopt ?ACK

图8:时间戳是在 3-way handshake 中协商的。SYN / SYN-ACK 中没有 TSopt,那条连接就不会使用它。

不看这一步就只去动 OS 的配置,就会招来“明明启用了却没生效”这种同样不起眼的问题。比起配置值,wire 上的事实更有分量。

5. RFC1323 时间戳为什么有效

实务中“RFC1323 的时间戳”这种叫法还在沿用,但现行的规范整理是 RFC 7323。本文按习惯写作 RFC1323,含义上指的是 TCP timestamps option。

5.1. 时间戳是为了 RTTM 与 PAWS

TCP 的 timestamps option 主要用于两个目的。

  • RTTM(Round-Trip Time Measurement)
  • PAWS(Protect Against Wrapped Sequences)

这次起作用的是 RTTM 这一侧。发送出去的分段带着 TSval,对方在 ACK 的 TSecr 中把它返回来,发送方就更容易把 RTT 测得更细、更准。

timestamps option的两个目的TCP的timestamps option用于RTTM也就是往返时延的测量和PAWS也就是序列号回绕的防护这两个目的,本案例中起作用的是RTTM一侧。timestamps optionRTTM(往返时延的测量)PAWS(序列号回绕的防护)本次起作用的是这一侧把TSval用ACK的TSecr返回来测量

图9:时间戳的目的是 RTTM 和 PAWS 两个。本案例中起作用的是 RTT 测量这一侧。

5.2. 可以消除重传时 RTT 测量的模糊性

一旦发生重传,没有时间戳的话,“这个 ACK 是针对最初的发送,还是针对重传”就变得模糊。这正是所谓 Karn 算法关心的那一点。

RFC 6298 规定,对于重传过的分段不得采集 RTT 样本。理由就是无法判断那是对哪一次发送的 ACK。不过有了 timestamps option,就可以消除这种模糊性。因为只要看 ACK 中带回来的 TSecr,就能识别出到达的是携带哪个 TSval 的分段。

接收方发送方接收方发送方这个分段丢失收不到 ACK 所以等待能判别这是对哪一次发送的响应Seq=N, TSval=1000重传 Seq=N, TSval=2000ACK, TSecr=2000

图10:只要看 TSecr,就能判别那个 ACK 是对最初的发送还是对重传的响应。

这就是本次改善的核心。

5.3. 本案例中能压缩等待时间的原因

在本案例中,丢包时不时发生,每次发生时 RTT / RTO 的估计都容易偏向保守。启用时间戳之后,即便在包含重传的场合也更容易更新 RTT 的估计值,从而缩短 RTO 估计一直陈旧地膨胀下去的时间。

这里是容易跳步的地方,再拆解得细一点。

首先,RTO 的下限本身,不会因为有没有时间戳而改变。 RFC 6298 规定,算出来的 RTO 如果小于 1 秒,就应当向上取整到 1 秒。实现也可能有自己的下限,Windows 上对应的就是 Get-NetTCPSetting 里出现的 MinRtoMs(以 10 毫秒为步长,取值范围 20~300 毫秒)。也就是说,并不是“加了时间戳所以下限降下来了”。

真正起作用的在这之前一步。RFC 6298 规定了下面 3 件事。

  1. 每次重传计时器超时,就把 RTO 翻倍(指数退避)
  2. 退避后的 RTO,在获得新的 RTT 测量值时回到原状
  3. 这个“新的 RTT 测量值”,只有在没有被重传过的数据被发送并得到 ACK 时才能拿到

再加上 Karn 算法,不得从重传过的分段上采集 RTT 样本。不过,使用 timestamps option 时这条约束就解除了。 正如 5.2 所述,只要看 TSecr 就能判别那是对哪一次发送的 ACK。

把这些摆在一起,脉络就清楚了。在丢包零星持续的系统里,没有时间戳时条件 2 和条件 3 迟迟凑不齐,翻倍后的 RTO 要靠实测回落,这段时间会拖得很长。 在这种状态下再来一次丢包,等待就不是从 1 秒开始,而是从 2 秒、4 秒开始。有了时间戳,包含重传的区间也能重新测量,于是这段“回不去的时间”变短——这就是本次真正起作用的路径。

翻倍后的RTO回落的条件与时间戳的作用方式每次超时RTO都会翻倍,获得新的RTT测量值时才回到原状,而这个测量值只能从没有被重传过的数据的ACK中取得,因此用timestamps解除这条约束后,翻倍的RTO回不去的时间就会缩短。每次超时RTO翻倍靠新的RTT测量值回到原状测量只能来自未重传数据的ACKtimestamps让重传区间也能测量重新测量的机会增多翻倍RTO回不去的时间缩短

图11:不是下限降下来了,而是翻倍后的 RTO 能靠实测更早回落,这才是起效的核心。

老实说,本文并没有测到改善之后 RTO 的实测值。 已知的只到“秒级停顿减少到现场不再构成问题的程度”这一步。如果想用数字说明效果,最近的路子是用第 7 章的显示过滤器和 Time delta from previous displayed packet 列,把到重传为止的时间差分布按 before / after 统计出来。

换句话说,这次做的并不是让 TCP 变快的魔法,而是 减少 TCP 超出必要地长时间观望的时间。

当然,RFC 7323 也没有说“只要 RTT 样本变多,什么问题都能漂亮地解决”。它对 RTO 优化的作用程度也有其局限的一面。不过,能消除重传时的模糊性 这一点,在本次这类系统上确实能直接见效。

也有需要注意的地方。

  • 这里有一部分取决于 TCP stack 的实现
  • 只靠时间戳并不能消除丢包本身
  • 如果物理层或中间设备有问题,根本原因就在别处
  • SACK、NIC 驱动、offload 配置、交换机侧的问题,最好也另外排查

不过,像这次这样“丢包并不为零”“但真正让人痛的是秒级等待”的系统,这种做法有时相当有效。

6. 实际采取的对策

6.1. 启用时间戳

作为对策,把连接两端都调整成能够协商 timestamps option 的状态。在 Windows 系统上,它有时会被当作 RFC 1323 选项来处理,并受到 OS 配置和网络配置的影响。

下面写出具体步骤。首先看当前是什么状态。

netsh interface tcp show global

输出里有 RFC 1323 时间戳的条目,确认那里是否已启用。要启用的话,在管理员权限的命令提示符下这样做。

netsh interface tcp set global timestamps=enabled

timestamps 可以指定 disabled / enabled / default 这 3 个值,default 表示恢复到系统默认。

用 PowerShell 查看和设置的话是这样。

Get-NetTCPSetting | Select-Object SettingName, Timestamps, MinRtoMs, InitialRtoMs
Set-NetTCPSetting -SettingName InternetCustom -Timestamps Enabled

Timestamps 可以指定 Enabled / Disabled。可修改的 SettingName 因 Windows 版本而异,所以不要直接敲 Set-,先用 Get-NetTCPSetting 确认实际存在的名称。传入不存在的名称就会在那里卡住。

在旧环境上直接看注册表的话,是下面这个键下的 Tcp1323Opts(REG_DWORD)。

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters

取值的含义是:0 表示禁用 RFC 1323 选项,1 表示只启用 window scaling,2 表示只启用 timestamps,3 表示两者都启用。记成位 0 对应 window scaling、位 1 对应 timestamps,就不会搞混。

设置时有 3 点需要注意。

  • 只对新建连接生效。 时间戳是在 3-way handshake 中协商的,所以对已经建立的连接无效。需要把设备侧的连接重新建立一遍
  • 两端都支持才能成立。 只在主机侧启用,如果相机侧不在 SYN/ACK 中回送 TSopt,那条连接上就不会使用
  • 配置也会改变连接的性质。 时间戳会让 TCP 首部变大,因此一个分段上能装的数据会略微减少

不过在实务中,比起“配置界面上显示为已启用”,“SYN / SYN-ACK 的实际数据包中带着 TSopt”更重要。这一点确实如此。

设置时间戳时的3点注意时间戳的配置是在3-way handshake中协商的所以只对新建连接生效,两端都支持才能成立,而且首部变大会让一个分段上能装的数据略微减少,这是三点注意事项。设置时的3点注意只对新建连接生效需要两端都支持首部变大数据略微减少把设备侧的连接重新建立

图12:光设置完还没结束。还需要重建连接,并确认对端是否支持。

6.2. 在 SYN / SYN-ACK 中确认 TSopt

启用之后确认了 3 点。

  • 出问题的连接的 SYN 中是否有 TSopt
  • SYN/ACK 一侧是否也回送了 TSopt
  • 之后的数据分段和 ACK 中是否持续带着 TSopt

确认了这些,才能说“那条连接上确实用上了 timestamps”。

6.3. 这样做仍然无效时该看哪里

即便启用了时间戳,遇到下面这些情况改善也可能很迟钝。

  • 丢包率本身就很高
  • 中间设备破坏、丢弃或篡改 TCP option
  • NIC / 驱动 / offload 一带另有问题
  • 应用把整体都挂在一次同步调用上,一次等待看起来就成了全面停止
  • 实际上主因不在 TCP,而是相机侧处理停滞或设备内部队列堵塞

所以,对策按这个顺序推进比较清楚。

  1. 先用 wire 确认是不是重传等待
  2. 查看 TSopt 有没有协商成功
  3. 启用 timestamps,看改善的差异
  4. 如果还有残留,就把丢包源和应用设计分开逐一深挖
推进对策的顺序先用wire确认是不是重传等待,再查看TSopt有没有协商成功,然后启用timestamps看改善的差异,如果还有残留就把丢包源和应用设计分开逐一深挖。用wire确认重传等待查看TSopt有没有协商启用timestamps看差异有残留就把丢包源和设计分开深挖

图13:按确认→协商→启用→剩余调查的顺序推进,就容易分辨出没生效的原因。

7. 在 Wireshark 中要看的要点

列出一些排查时好用的显示过滤器。

tcp.stream eq <目标流>
tcp.analysis.retransmission
tcp.analysis.fast_retransmission
tcp.analysis.lost_segment
tcp.options.timestamp.tsval
tcp.options.timestamp.tsecr

看的时候有几个诀窍。

  • 用 tcp.stream 把范围缩到目标连接
  • 把 Time delta from previous displayed packet 显示出来,直接看停顿了几秒
  • 确认问题发生的瞬间是否出现了 Retransmission
  • 确认连接建立时的 SYN / SYN-ACK 是否协商了 TSopt
  • 看 ACK 的 TSecr 有没有回来

界面上具体是什么样子,也用文字写下来。这个症状看惯了之后一眼就能认出来。

看哪里 看起来是什么样
数据包列表的 Info 列 重传的数据包上会带有 [TCP Retransmission]
行的颜色 它命中 Wireshark 默认的着色规则“Bad TCP”,所以那一行会变成黑底红字。快速浏览也能捡出来
详情窗格 选中对应的行展开 Transmission Control Protocol,再打开其中的 SEQ/ACK analysis,就会显示它被判定为重传
时间差 把 Time delta from previous displayed packet 加为一列,就能读出这一行距离上一个显示的数据包隔了多少秒。这里会排出 1 秒、2 秒这样的值
SYN 的 Options 选中 SYN 的行展开 Options,看有没有 Timestamps 条目,就知道有没有 TSopt

典型的排列方式是这样:先是同一个 Seq 的行在 1 秒后出现,再过 2 秒又出现一次,最后那一行之后紧接着返回 ACK,从那里恢复正常的交互。如果这段“空白的数秒”与应用侧日志中响应停住的时间一致,元凶基本就能定下来。

把日志和数据包对照时,还要注意应用时刻与抓包时刻的基准差。这里一旦错位,就容易把别的事情当成元凶。

在Wireshark中确定元凶的流程用tcp.stream把范围缩到目标连接,用时间差列直接看停顿了几秒,如果这段空白的数秒与应用日志中响应停住的时间一致,元凶基本就能定下来。是否用tcp.stream缩到目标连接用时间差列看空白的秒数确认同一个Seq的重传排在一起与应用的停顿时长一致?元凶基本确定怀疑时刻基准差或其他因素

图14:缩范围、看时间差、做对照。这 3 步就能确定“空白的数秒”的真身。

8. 大致的判断分工

症状 首先怀疑的对象 最先要做的事
以数秒为单位偶尔停顿 TCP 的 RTO 等待 用数据包确认重传与时间差
每次几乎在相同时机停顿 应用内部等待、设备侧处理、固定超时 查看线程、SDK 调用、设备日志
只在高负载时恶化 CPU、GC、队列堵塞 查看 CPU、中断、内存、队列长度
大范围连接同时变差 物理层、交换机、中间设备 查看 NIC、线缆、端口统计、中间设备日志
改了配置却没有变化 TCP option 没有协商成功 重新确认 SYN / SYN-ACK

最后一行的情况真的非常多。改动配置带来的满足感,和 wire 上确实在使用这个事实,是两回事。

9. 总结

本次的要点:

  • “偶尔停顿数秒”有时并不是应用停止,而是 TCP 在等重传
  • 如果停顿时长与 RTO 的等待方式吻合,并且能看到 Retransmission,那这条思路相当靠谱
  • TCP timestamps option 是为 RTTM 和 PAWS 设计的机制,也能消除重传时 RTT 测量的模糊性
  • 在本案例中,启用 RFC1323 系列时间戳之后,缩短了 RTO 一直停留在过度保守状态的时间

想避免的做法:

  • 只凭应用日志就判定通信停顿的元凶
  • 只看 OS 配置,不看实际数据包
  • 以为启用时间戳就能连丢包原因一起消掉

实务中有效的做法:

  • 先看 wire
  • 确认重传与等待时间的形态
  • 确认 TSopt 的协商情况
  • 改善之后,丢包源和应用设计仍要分开深挖

也就是说,这类故障“判断在哪里等待”比“让它变快”更靠前。只要不在这一点上跑偏,排查就会大幅缩短。

10. 参考资料

共享相同标签的最新文章。可以围绕相近的主题进一步加深理解。

与本文相近的主题页面。以本文为起点,可以进一步了解相关服务和其他文章。

在梳理与改进方式上相近的案例页面。

本文与以下服务页面相关联,欢迎从最接近的入口查看。

常见问题

汇总了咨询这一主题时常见的问题。

什么是 TCP Retransmission(TCP 重传)?
这是指发送出去的数据包没有收到对应的 ACK(确认应答)时,TCP 重新发送同一份数据的机制。如果数据包在传输途中的某处丢失,发送方会等待 ACK,一直等不到时就会等重传计时器(RTO)超时后再重传。从应用程序的角度看,这会表现为“停顿了几秒”,但从 TCP 的角度说,只是“还没收到 ACK,所以在等重传计时器超时”而已,这种停顿方式其实很常见。在抓包中可以观测为 TCP Retransmission。
TCP Retransmission 发生的原因是什么?
直接的触发原因是丢包,因为 ACK 没有返回才发生重传。至于丢包的来源,需要分别排查物理层(线缆或端口)、NIC 及其驱动与 offload 配置、交换机或中间设备等问题。启用 TCP timestamps 等手段确实可以压缩重传后的等待时间,但它不是消除丢包本身的魔法,所以仍然需要单独排查丢包源。此外还有中间设备破坏或丢弃 TCP 选项的情况,也有主因其实不在 TCP,而是设备侧处理停滞的情况。
为什么 TCP 重传会让通信停顿长达几秒?
这是因为 TCP 的重传计时器(RTO)采用了保守的等待方式。RFC 6298 规定初始 RTO 以 1 秒为基准,计算结果小于 1 秒也要向上取整到 1 秒,而且每次超时都会翻倍。因此在条件不利时,等待会呈现 1 秒、2 秒、4 秒这样的模式。在以小型 request/response 为主的控制通信中,往往还来不及收集到足够的 duplicate ACK 去触发 fast retransmit,RTO 等待就先暴露出来,于是表现为偶发的数秒停顿。启用 TCP timestamps option 后,可以消除重传时 RTT 测量的模糊性,从而在某些情况下缩短 RTO 估计过度保守而迟迟不回落的时间。
在 Wireshark 中该怎样排查 TCP Retransmission?
可以使用 tcp.analysis.retransmission、tcp.analysis.fast_retransmission、tcp.analysis.lost_segment 等显示过滤器。先用 tcp.stream 把范围缩到目标连接,再显示 Time delta from previous displayed packet 直接确认停顿了几秒,然后看问题发生的瞬间是否出现 Retransmission,以及到重传为止的时间差是否与停顿时长一致。是否用上了 TCP timestamps,要看连接建立时的 SYN / SYN-ACK 是否协商了 TSopt。比起配置界面上显示为已启用,实际数据包里确实带着 TSopt 这个事实更重要。

作者简介

本文作者的个人简介页面。

Go Komura

小村软件有限公司 代表

以 Windows 软件开发、技术咨询与故障排查为中心,擅长难以复现的故障调查,以及既有资产仍在运行的项目。

返回博客列表