更新履歴(5件・最終更新 2026年08月22日)
この記事に加えた変更の記録です。アーカイブした更新前のバージョンは、DOI付きの固定URLから読めます。
- 切り分けの順序やRTOが戻る仕組みを図でも追えるように、Mermaid図を10点追加しました(地の文500〜750字につき1図の規約に合わせたものです)。既存の図には通し番号付きのキャプションを付けました。本文の文章は変えていません。
- 記事の冒頭に「この記事の知識マップ」節を追加しました。本文で扱っている概念とその関係を、要約・図・詳細ページへのリンクにまとめたものです。本文の主張は変えていません。
- 外部レビュー(1283件)への対応として本文を更新しました。個々の変更内容は、この下の履歴を参照してください。
- タイムスタンプを有効にする具体手順を追加しました(`netsh`とPowerShellの`Set-NetTCPSetting`、レジストリの`Tcp1323Opts`、新規接続からしか効かない等の注意3点)。用語表を追加し、RTOの下限はタイムスタンプの有無では変わらないことを明記したうえで、実際に効いている筋(バックオフしたRTOが新しいRTT測定で戻る、その測定は再送していないセグメントでしか得られない)を説明しました。Wiresharkの画面で何がどう見えるかの表も追加しています。
- 参考リンクなどで縦棒(パイプ)記号を含む行が表として表示され、リンクが押せなくなっていた表示崩れを修正しました。本文の内容は変えていません。
- 初版公開
この記事を引用する(DOI(登録済みアーカイブ): 10.5281/zenodo.21589605)
以下のDOIは過去に登録されたアーカイブを指しており、現在の本文とは一致しない場合があります。現在の本文を参照するときは、このページのURLを使用してください。
小村 豪(2026)「TCP再送で産業用カメラ通信が止まる原因と切り分け」合同会社小村ソフト. https://comcomponent.com/blog/2026/03/11/001-tcp-retransmission-rfc1323-industrial-camera/
- DOI(登録済みアーカイブ)
- 10.5281/zenodo.21589605
- DOI(前回登録した版)
- 10.5281/zenodo.21732631
産業用カメラや装置制御の通信では、平均では速いのに たまに数秒止まる という現象がいちばん厄介です。再現率が低く、普段は何も起きないので、UI、スレッド、GC、カメラ SDK、NIC、スイッチ、全部が少しずつ怪しく見えてきます。
今回扱うのは、産業用カメラを制御するアプリとホストの TCP 通信で、まれに数秒間だけ通信が止まる事象です。調べてみると、正体はアプリの停止ではなく、パケットロスに起因する TCP の再送待ち でした。さらに、RFC1323 系のタイムスタンプ機能(現在の整理では RFC 7323)を有効にすることで、この系では待ち時間を最小限に寄せられました。
装置名や構成、数値は一般化していますが、考え方はそのまま実務で使えます。
目次
- まず結論(ひとことで)
- 症状の見え方
- 2.1. アプリは生きているのに応答だけ数秒止まる
- 2.2. 低頻度なのでログだけだと見えにくい
- 何が起きていたか(図)
- 3.1. パケットロスから再送待ちに入る
- 3.2. 秒単位停止と RTO の形が合っていた
- 調査で見たポイント
- 4.1. まずアプリ内の停止要因を外す
- 4.2. パケットキャプチャで再送を確認する
- 4.3. 交渉されている TCP オプションを見る
- RFC1323 タイムスタンプが効く理由
- 5.1. タイムスタンプは RTTM と PAWS のため
- 5.2. 再送時の RTT 測定の曖昧さを外せる
- 5.3. この事例で待機時間を詰められた理由
- 実際にやった対策
- 6.1. タイムスタンプを有効化する
- 6.2. SYN / SYN-ACK で TSopt を確認する
- 6.3. それでも効かないときに見る場所
- Wireshark で見るポイント
- ざっくり使い分け
- まとめ
- 参考資料
図の実線は常に成り立つ関係、破線は条件付きの関係です(成立条件は詳細ページの各関係の説明に記載)。関係すべての一覧(全19件、根拠・確度つき)と主要概念の定義は知識マップ詳細ページにまとめています。データ: JSON-LD / Turtle
1. まず結論(ひとことで)
- まれに数秒止まる TCP 通信は、アプリ停止ではなく パケットロス後の再送待ち が正体のことがあります
- パケットキャプチャで
Retransmissionと大きな時間差が見え、停止時間が RTO の待ち方と噛み合うなら、かなり怪しいです - TCP timestamps option は RTT 測定と PAWS のための仕組みで、再送時の RTT 測定の曖昧さも外せます
- この事例では、RFC1323 系のタイムスタンプ機能を有効にすることで、RTO 推定が古く保守的なまま残る時間を減らし、秒単位の停止を最小限に寄せられました
- ただし、これは ロスそのものを消す魔法 ではありません。物理層、NIC、スイッチ、中間機器、ドライバ、バッファ設計の見直しは別に必要です
要するに、「たまに数秒止まる」の正体が TCP の中の待ち時間なら、アプリのリトライだけ頑張っても芯を外します。まず wire を見て、再送待ちかどうかを確定させたほうが早いです。
flowchart TB
accTitle: この記事の結論の流れ
accDescr: まれに数秒止まるTCP通信はまずwireを見て再送待ちかどうかを確定させ、再送待ちならtimestampsで待ち時間を詰められる場合があるが、パケットロスそのものの調査は別に必要であることを示す。
sym["まれに数秒止まるTCP通信"] --> wire["まずwireを見る"]
wire --> conf["再送待ちかどうかを確定させる"]
conf -.-> ts["timestampsで待ちを詰められる場合あり"]
conf -.-> loss["ロス源の調査は別に必要"]
図1: アプリのリトライを頑張る前に、まずwireで「どこで待っているか」を当てる。
この先、TCP の略語が続けて出てきます。ネットワークが専門でない Windows 開発者向けに、先にまとめておきます。ここだけ押さえておけば、以降は詰まらないはずです。
| 用語 | 正式名 | 意味 |
|---|---|---|
| ACK | Acknowledgment | 確認応答。「ここまで受け取った」を相手に知らせる仕組みです |
| RTT | Round-Trip Time | 往復時間。送ってから ACK が返るまでの時間です |
| RTO | Retransmission Timeout | 再送タイマ。この時間内に ACK が返らなければ再送します。RTT の推定値から計算されます |
| RTTM | Round-Trip Time Measurement | RTT の測定。タイムスタンプ機能の目的の 1 つです |
| PAWS | Protect Against Wrapped Sequences | シーケンス番号が一周してしまう事故への対策。タイムスタンプ機能のもう 1 つの目的です |
| TSopt | TCP Timestamps Option | TCP ヘッダーに付くオプションの 1 つ。種別 8、長さ 10 バイトで、次の 2 つの値を運びます |
| TSval | Timestamp Value | 送信側が入れる、自分側の時計の値です |
| TSecr | Timestamp Echo Reply | 受け取った TSval をそのまま返す欄。これで「どの送信に対する ACK か」が分かります |
| SACK | Selective Acknowledgment | 受信側が「どの範囲を受け取ったか」を個別に伝える仕組みです |
| Dup ACK | Duplicate ACK | 同じシーケンス番号を指す ACK が重複して返ること。途中に抜けがある合図です |
| fast retransmit | — | Dup ACK が一定数たまったら、RTO の満了を待たずに再送する仕組み。Windows の既定は「同じシーケンス番号の ACK を 3 つ(最初の 1 つと重複 2 つ)受け取ったら」です |
| Karn のアルゴリズム | — | 再送したセグメントからは RTT サンプルを取ってはいけない、という規則。どの送信への ACK か分からないためです |
2. 症状の見え方
2.1. アプリは生きているのに応答だけ数秒止まる
最初にややこしいのは、アプリ全体が固まっているようには見えないことです。
- UI は完全には死んでいない
- プロセスも落ちていない
- CPU も貼り付いていない
- ただし、カメラ制御コマンドの応答だけが たまに 数秒抜ける
こういう症状は、アプリ内の deadlock や無限ループとも見分けがつきにくいです。しかも装置制御では、1 回の数秒停止がそのままライン停止の印象になります。平均値がきれいでも、現場の体感はだいぶ悪いです。
2.2. 低頻度なのでログだけだと見えにくい
このタイプの不具合が面倒なのは、発生頻度が低いことです。1 時間に 1 回、半日に 1 回、条件が重なったときだけ、みたいな振る舞いをします。
ログだけで追うと、だいたいこうなります。
- アプリログ上は「送信した」「返ってこない」で止まる
- 受信側ログは「何も来ていない」に見える
- たまたま同じ時間帯に別のイベントも起きて、犯人が散る
こういうとき、アプリログだけで因果関係を復元しようとすると、わりと普通に沼ります。通信層まで一段下りたほうが早いです。
flowchart TB
accTitle: ログだけでは犯人が決まらない構図
accDescr: アプリログは送信したが返ってこないで止まり、受信側ログは何も来ていないに見え、同じ時間帯の別イベントで犯人が散るため、アプリログだけで因果を復元しようとせず通信層まで下りたほうが早いことを示す。
l1["アプリログ:送信したが返らない"] --> lost["犯人が散って沼る"]
l2["受信側ログ:何も来ていない"] --> lost
l3["同時間帯の別イベント"] -.-> lost
lost --> down["通信層まで一段下りる"]
図2: 低頻度の停止はログだけで追うと沼る。パケットで見るほうが早い。
3. 何が起きていたか(図)
3.1. パケットロスから再送待ちに入る
今回の筋はシンプルです。途中のどこかでパケットが落ち、送信側が ACK を待ち、来ないので RTO を待ってから再送していました。
sequenceDiagram
participant Host as ホストアプリ
participant Net as ネットワーク
participant Cam as カメラ側
Host->>Net: 制御コマンド (Seq=N)
Note over Net: ここでロス
Note over Host: ACK が来ないので待つ
Note over Host: この要求の回復には RTO 待ちが入る
Host->>Net: 制御コマンドを再送
Net->>Cam: 再送パケット到達
Cam-->>Net: ACK
Net-->>Host: ACK
Note over Host: ここで通信再開
図3: パケットが落ちるとACKが返らず、RTOの満了を待ってから再送する。アプリにはこの間が「数秒の停止」に見える。
アプリから見ると「数秒止まった」ように見えますが、TCP 的には「まだ ACK が来ていないので、再送タイマの満了を待っていた」というだけです。地味ですが、こういう止まり方は普通にあります。
今回の制御通信は、小さな request/response が多く、1 回のやり取りで大量の未 ACK データが飛んでいませんでした。そのため duplicate ACK を十分に集めて fast retransmit に乗るより先に、RTO 待ち が表に出やすい構成でした。
flowchart TB
accTitle: 制御通信でRTO待ちが表に出やすい理由
accDescr: 小さなrequest/response中心の制御通信では未ACKデータが少なく、duplicate ACKが十分に集まらないためfast retransmitに乗りにくく、その結果RTO待ちが表に出やすいことを示す。
small["小さなrequest/response中心"] --> few["未ACKデータが少ない"]
few --> nodup["Dup ACKが十分に集まらない"]
nodup --> nofr["fast retransmitに乗りにくい"]
nofr --> rto["RTO待ちが表に出る"]
図4: 大量のデータが流れる通信と違い、制御通信のロスはRTO満了待ちになりやすい。
3.2. 秒単位停止と RTO の形が合っていた
TCP の再送待ちは、実装差はあるものの、保守的な待ち方をします。RFC 6298 では、初期 RTO は 1 秒が基準で、計算結果がそれより小さければ 1 秒に切り上げ、タイムアウトが起きたら倍化します。
flowchart LR
A[パケットロス] --> B[ACK が来ない]
B --> C[RTO 待ち]
C --> D[再送]
D --> E{ACK が返る?}
E -- はい --> F[通信再開]
E -- いいえ --> G[RTO を倍化]
G --> C
図5: RTOはACKが返らないたびに倍化する。1秒・2秒・4秒という待ち方はこの形から来る。
なので、数百ミリ秒で終わってほしい局面でも、条件が悪いと 1 秒、2 秒、4 秒のような待ち方に見えることがあります。今回の「まれに数秒止まる」は、この形とかなり素直に噛み合っていました。
4. 調査で見たポイント
4.1. まずアプリ内の停止要因を外す
いきなり TCP と決め打ちせず、先にアプリ側の典型要因を外しました。
| 確認したもの | 見た理由 | 今回の結論 |
|---|---|---|
| UI スレッド / ワーカースレッド | ハングや相互待ちの確認 | 主因ではなかった |
| CPU 使用率 | 高負荷による処理遅延の確認 | 停止時も張り付きではなかった |
| GC / メモリ圧迫 | 一時停止の確認 | 停止時間の形が合わなかった |
| カメラ SDK 呼び出し | SDK 内待機の確認 | wire 上の遅延と一致しなかった |
| パケットキャプチャ | 通信層の再送確認 | ここで原因の筋が見えた |
ここで大事なのは、アプリログの時刻だけで犯人を決めないことです。装置制御アプリでは、上位の待機は下位の待機をただ映していることがあります。
flowchart TB
accTitle: アプリ内要因を先に外す切り分け順
accDescr: いきなりTCPと決め打ちせず、スレッドやCPUやGCやカメラSDKといったアプリ内の典型的な停止要因を先に外してから、パケットキャプチャで通信層の再送を確認するという切り分けの順序を示す。
app["スレッド・CPU・GC・SDKを先に外す"] --> cap["パケットキャプチャで通信層を見る"]
cap --> found["再送の筋が見えてくる"]
app -.-> warn["ログの時刻だけで犯人を決めない"]
図6: 決め打ちしない。アプリ内の典型要因を外してから、wireへ下りる。
4.2. パケットキャプチャで再送を確認する
パケットキャプチャを取ると、停止している時間帯で TCP Retransmission が見え、さらにその直前に ACK が返ってきていないことが分かりました。
見るべき点はこのあたりです。
- 同じ
Seqの再送が出ているか - 再送までの時間差が停止時間と一致するか
Dup ACKやFast Retransmissionではなく、RTO 満了待ちに見えるか- 問題の接続が毎回同じ
tcp.streamに出るか
ここが合うと、「アプリが止まっている」のではなく、「TCP が再送待ちをしている」がかなり濃くなります。
flowchart TB
accTitle: 再送待ちを確定させる確認点
accDescr: 同じSeqの再送が出ているか、再送までの時間差が停止時間と一致するか、fast retransmissionではなくRTO満了待ちに見えるかの3点が合うと、アプリの停止ではなくTCPの再送待ちという見立てが濃くなることを示す。
c1["同じSeqの再送が出ているか"] --> conc["TCPが再送待ちをしている"]
c2["時間差が停止時間と一致するか"] --> conc
c3["RTO満了待ちに見えるか"] --> conc
conc -.-> not["アプリが止まっているのではない"]
図7: この3点が噛み合えば、「アプリ停止」ではなく「TCPの再送待ち」がかなり濃くなる。
4.3. 交渉されている TCP オプションを見る
次に見たのが、接続開始時の SYN / SYN-ACK です。タイムスタンプは TCP 接続の 3-way handshake で交渉されるので、ここに TSopt が出ていなければ、その接続では使われません。
sequenceDiagram
participant Host as ホスト
participant Cam as カメラ側
Host->>Cam: SYN + TSopt ?
Cam-->>Host: SYN/ACK + TSopt ?
Host->>Cam: ACK
Note over Host,Cam: ここで交渉できて初めて以降のセグメントで TSopt が使える
図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 は、主に 2 つの目的で使われます。
- RTTM(Round-Trip Time Measurement)
- PAWS(Protect Against Wrapped Sequences)
ここで今回効いたのは RTTM 側です。送信したセグメントの TSval を、相手が ACK の TSecr で返すことで、送信側は RTT をより細かく、より正確に測りやすくなります。
flowchart TB
accTitle: timestamps optionの2つの目的
accDescr: TCPのtimestamps optionはRTTMすなわち往復時間の測定と、PAWSすなわちシーケンス番号の一周対策の2つの目的で使われ、今回の事例で効いたのはRTTM側であることを示す。
tso["timestamps option"] --> rttm["RTTM(往復時間の測定)"]
tso --> paws["PAWS(シーケンス番号一周対策)"]
rttm --> hit["今回効いたのはこちら"]
rttm -.-> how["TSvalをACKのTSecrで返して測る"]
図9: タイムスタンプの目的はRTTMとPAWSの2つ。この事例で効いたのはRTT測定の側。
5.2. 再送時の RTT 測定の曖昧さを外せる
再送が入ると、タイムスタンプなしでは「この ACK は最初の送信に対するものか、再送に対するものか」が曖昧になります。これがいわゆる Karn のアルゴリズムが気にしている点です。
RFC 6298 では、再送されたセグメントでは RTT サンプルを取ってはいけない、とされています。理由は、どの送信に対する ACK か分からないからです。ただし、timestamps option があると、この曖昧さを外せます。ACK に入ってくる TSecr を見れば、どの TSval を持ったセグメントが届いたかを識別できるからです。
sequenceDiagram
participant Host as 送信側
participant Cam as 受信側
Host->>Cam: Seq=N, TSval=1000
Note over Host,Cam: このセグメントがロス
Note over Host: ACK が来ないので待つ
Host->>Cam: Seq=N, TSval=2000 を再送
Cam-->>Host: ACK, TSecr=2000
Note over Host: どの送信への応答か判別できる
図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 つを定めています。
- 再送タイマが切れるたびに、RTO を 2 倍にする(指数バックオフ)
- バックオフした RTO は、新しい RTT 測定が得られたときに元へ戻る
- その「新しい RTT 測定」は、再送していないデータが送られて ACK されたときにしか得られない
さらに Karn のアルゴリズムにより、再送したセグメントからは RTT サンプルを取ってはいけません。ただし、この制約は timestamps option を使っている場合には外れます。 5.2 のとおり、TSecr を見ればどの送信への ACK か判別できるからです。
ここまで並べると筋が見えます。ロスが散発的に続く系では、タイムスタンプなしだと 2 と 3 の条件がなかなか揃わず、倍化したままの RTO が実測に基づいて戻るまでの時間が長く残ります。 その状態で次のロスが来ると、1 秒ではなく 2 秒、4 秒という待ちから始まってしまう。タイムスタンプがあると、再送を含む区間でも測り直せるので、この「戻れない時間」が短くなる、というのが今回効いた道筋です。
flowchart TB
accTitle: 倍化したRTOが戻る条件とタイムスタンプの効き方
accDescr: タイムアウトのたびにRTOは倍化し、新しいRTT測定が得られたときに元へ戻るが、その測定は再送していないデータのACKからしか得られないため、timestampsでこの制約が外れると倍化したRTOが戻れない時間が短くなることを示す。
backoff["タイムアウトごとにRTOが倍化"] --> ret["新しいRTT測定で元に戻る"]
ret -.-> limit["測定は未再送データのACKからのみ"]
ts2["timestampsで再送区間でも測れる"] --> often["測り直しの機会が増える"]
often --> short["倍化RTOが戻れない時間が縮む"]
図11: 下限が下がるのではなく、倍化したRTOが実測で早く戻るようになるのが効き方の芯。
正直に書いておくと、この記事では改善後の RTO の実測値までは取っていません。 分かっているのは、秒単位の停止が現場で問題にならない程度まで減った、というところまでです。効果を数字で示したい場合は、7 章の表示フィルタと Time delta from previous displayed packet 列を使って、再送までの時間差の分布を before / after で集計する のがいちばん近道です。
言い換えると、今回やったのは TCP を速くする魔法ではなく、TCP が必要以上に長く様子見し続ける時間を減らすこと です。
もちろん、RFC 7323 も「RTT サンプルが増えれば何でもきれいに解決する」とは言っていません。RTO の最適化に効く度合いは限定的な面もあります。ただ、再送時の曖昧さを外せる という点は、今回のような系で素直に効くことがあります。
注意点もあります。
- これは TCP stack の実装に依存する部分があります
- タイムスタンプだけでパケットロス自体は消えません
- 物理層や中間機器が悪ければ、根本原因は別にあります
- SACK や NIC ドライバ、オフロード設定、スイッチ側の問題も別で見たほうがよいです
ただ、今回のように「ロスはゼロではない」「でも本当に痛いのは秒単位の待ち」という系では、かなり効くことがあります。
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 ヘッダーを大きくするので、1 セグメントに載るデータは少し減ります
ただし、実務では「設定画面で有効になっている」より、「SYN / SYN-ACK の実パケットに TSopt が乗っている」のほうが大事です。ここは本当にそうです。
flowchart TB
accTitle: タイムスタンプ設定時の注意3点
accDescr: タイムスタンプの設定は3-way handshakeで交渉されるため新規接続からしか効かず、両端が対応していないと成立せず、ヘッダーが大きくなって1セグメントに載るデータが少し減るという3つの注意点を示す。
care["設定時の注意3点"] --> n1["効くのは新規接続から"]
care --> n2["両端の対応が必要"]
care --> n3["ヘッダーが増えデータは少し減る"]
n1 -.-> re["装置側の接続を張り直す"]
図12: 設定しただけでは終わらない。接続の張り直しと、相手側の対応の確認が要る。
6.2. SYN / SYN-ACK で TSopt を確認する
有効化後は 3 点を確認しました。
- 問題の接続の SYN に TSopt があるか
- SYN/ACK 側も TSopt を返しているか
- 以降のデータセグメントと ACK にも TSopt が継続して載っているか
ここが確認できて初めて、「その接続で timestamps が実際に使われている」と言えます。
6.3. それでも効かないときに見る場所
タイムスタンプを有効にしても、こういう場合は改善が鈍いことがあります。
- ロス率そのものが高い
- 中間機器が TCP option を壊す、落とす、変形する
- NIC / ドライバ / オフロード周りに別の問題がある
- アプリが 1 本の同期呼び出しに全体をぶら下げていて、1 回の待ちがそのまま全停止に見える
- 実際には TCP ではなく、カメラ側の処理停止や装置内キュー詰まりが主因
なので、対策はこの順番で進めるのが分かりやすいです。
- まず wire で再送待ちを確認する
- TSopt の交渉有無を見る
- timestamps を有効にして改善差分を見る
- まだ残るなら、ロス源とアプリ設計を別々に詰める
flowchart TB
accTitle: 対策を進める順番
accDescr: まずwireで再送待ちを確認し、TSoptの交渉有無を見て、timestampsを有効にして改善差分を見て、それでも残るならロス源とアプリ設計を別々に詰めるという対策の順番を示す。
s1["wireで再送待ちを確認"] --> s2["TSoptの交渉有無を見る"]
s2 --> s3["timestampsを有効にして差分を見る"]
s3 --> s4["残るならロス源と設計を別々に詰める"]
図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 が返り、そこから通常のやり取りが再開します。この「空白の数秒」と、アプリ側のログで応答が止まっていた時間が一致すれば、犯人はほぼ確定です。
ログとパケットを付き合わせるときは、アプリ時刻とキャプチャ時刻の基準差にも注意が必要です。ここがずれると、別件を犯人扱いしがちです。
flowchart TB
accTitle: Wiresharkで犯人を確定させる流れ
accDescr: tcp.streamで対象接続だけに絞り、時間差の列で止まっている秒数を直接見て、その空白の数秒がアプリログで応答が止まっていた時間と一致すれば犯人はほぼ確定という流れを示す。
filt["tcp.streamで対象接続に絞る"] --> delta["時間差の列で空白の秒数を見る"]
delta --> re["同じSeqの再送が並ぶのを確認"]
re --> match{"アプリの停止時間と一致?"}
match -->|"はい"| fix["犯人はほぼ確定"]
match -->|"いいえ"| other["時刻の基準差や別要因を疑う"]
図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. 参考資料
- RFC 1323 - TCP Extensions for High Performance
- RFC 7323 - TCP Extensions for High Performance
- RFC 5681 - TCP Congestion Control
- RFC 6298 - Computing TCP’s Retransmission Timer
- Description of Windows TCP features - Windows Server | Microsoft Learn
- Netsh commands for Interface Transmission Control Protocol - Microsoft Learn
- Set-NetTCPSetting - Microsoft Learn
- Get-NetTCPSetting - Microsoft Learn
- Wireshark User’s Guide - Time Display Formats And Time References
- Wireshark User’s Guide - Packet Colorization
関連する記事
同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。
ネットワークドライブとUNCパスの落とし穴 ── 業務アプリでファイルサーバー(共有フォルダ)を扱う実務
業務アプリから共有フォルダへ出力・監視するときの定番トラブルを整理します。ドライブ文字(Z:)がサービスから見えない理由、実行アカウントごとに必要な権限、エラー1219、FileSystemWatcherの注意点まで解説します。
Application Verifierで作るWindows異常系テスト基盤
Application Verifier とは何かを、Handles、Heaps、Low Resource Simulation、!htrace を使った Windows の異常系テスト基盤づくりとあわせて整理します。
産業用カメラ長期稼働クラッシュ調査 - ハンドルリーク編
Windows アプリが長時間運転後に突然落ちるときの見方を、産業用カメラ制御アプリの事例を使って、ハンドルリークの見つけ方とログ設計の観点から整理します。
ネットは使えるのに「インターネットなし」?── WindowsのNCSI・DNS・プロキシ・VPNを切り分ける
ネットは使えるのにWindowsが「インターネットなし」と表示する理由を、NCSIの接続判定から解説します。DNS・プロキシ・VPN・認証Wi-Fiを、設定変更前の確認コマンドとイベントログで切り分けます。
Time Travel Debugging ── 長期稼働で再現しない不具合を「録画」して巻き戻す
月に一度しか出ない不具合は、クラッシュダンプでは結果しか写りません。WinDbgのTime Travel Debugging(TTD)で実行を録画して巻き戻す方法を、TTD.exeの録画設計、リングバッファ、TTD.Callsクエリ、ダンプとの使い分けまで解説します。
関連トピック
このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。
Windows技術トピック
Windows 開発、不具合調査、既存資産活用の技術トピックをまとめた入口です。
不具合調査 / 長期稼働テーマ
再現しにくい不具合、通信停止、長期稼働障害、失敗パス検証を整理するトピックです。
このテーマがつながるサービス
この記事は次のサービスページにつながります。近い入口からご覧ください。
不具合調査・原因解析
再現しにくい通信停止をパケットとエビデンスで切り分ける話なので、不具合調査・原因解析 に直結するテーマです。
Windowsアプリ開発
装置連携を含む Windows アプリとして、通信設計や監視を実装側から見直す相談にもつながります。
よくある質問
この記事のテーマについて、相談時によくある質問をまとめています。
- TCP Retransmission(TCP再送)とは何ですか?
- 送信したパケットに対する ACK(確認応答)が返ってこないとき、TCP が同じデータを再度送信する仕組みです。途中のどこかでパケットが落ちると、送信側は ACK を待ち、来ない場合は再送タイマ(RTO)の満了を待ってから再送します。アプリから見ると「数秒止まった」ように見えますが、TCP 的には「まだ ACK が来ていないので再送タイマの満了を待っていた」だけ、という止まり方は普通にあります。パケットキャプチャでは TCP Retransmission として観測できます。
- TCP Retransmissionが発生する原因は何ですか?
- 直接の引き金はパケットロスで、ACK が返らないために再送が起きます。ロスの発生源としては、物理層(ケーブルやポート)、NIC やそのドライバ・オフロード設定、スイッチや中間機器の問題などを個別に見る必要があります。なお TCP timestamps の有効化などで再送後の待ち時間を詰めることはできますが、ロスそのものを消す魔法ではないため、ロス源の調査は別に必要です。中間機器が TCP オプションを壊す・落とすケースや、実は TCP ではなく装置側の処理停止が主因というケースもあります。
- TCP再送で通信が数秒も止まるのはなぜですか?
- TCP の再送タイマ(RTO)が保守的な待ち方をするためです。RFC 6298 では初期 RTO は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 が乗っている事実のほうが重要です。