総合演習 — TCP の動きをパケットの気持ちで読み解く
実務の障害調査を模した 8 つのケースを、症状 → どの約束の話か → 見るべき数字、の順で切り分ける。つまずいたら対応する章へ戻って復習する。
仕上げの 8 問 — 症状を「どの約束の話か」に翻訳する
最終章は解説なしの総合演習です。実務の障害調査を模した 8 つのケースに、全 6 章の知識を横断して答えます。迷ったら、この講座の合言葉に立ち返ってください — いま破られかけているのは、到達の約束か? 順序か? ペース合わせか? そして症状を数字に翻訳してください: RST か沈黙か、重複 ACK の番号はどこを指すか、min を取っている窓はどれか。
| つまずいたら | 戻る章 |
|---|---|
| IP の性質・TCP/UDP の分担・4 つ組 | 第 1 章 |
| ハンドシェイク・TIME_WAIT・RST と FIN | 第 2 章 |
| seq/ack の計算・RTO・重複 ACK と高速再転送 | 第 3 章 |
| ウィンドウ広告・in-flight・ウィンドウ ÷ RTT・ゼロウィンドウ | 第 4 章 |
| cwnd・スロースタート・ロスへの 2 段階の反応 | 第 5 章 |
| バイトストリーム・フレーミング | 第 6 章 |
この講座で作った切り分けの地図。症状 → パケットの証拠 → どの約束の話か、の順に降りると、当てずっぽうの再起動から卒業できます。
ケース 7-1 — 繋がらない・切れる
第 1〜2 章の範囲です。「即座の拒否か、沈黙か」「FIN か RST か」に翻訳してください。
Q1. ケース: 新しくデプロイした API サーバに接続できません。クライアント A からは Connection refused が即座に返り、クライアント B からは30 秒待って接続タイムアウトになります。それぞれの状況の読みとして正しいものはどれですか。
即座の refused は RST が返った証拠 — ホストには届いているのに、そのポートで誰も待ち受けていない(プロセス未起動、ポート番号違いなど)。沈黙の末のタイムアウトは応答が返ってこない — 経路断、ホスト停止、または黙って捨てるファイアウォールです。A の存在から「ホストは生きていてポートに問題」「B との経路のどこかで遮断」まで絞れます。届いた上での拒否と、届かない沈黙 — 第 2 章の切り分けの型です。
Q2. ケース: ロードバランサ経由でバックエンドへ大量の短い接続を張るプロキシサーバで、netstat を見ると TIME_WAIT 状態のコネクションが数万件並んでいました。この状況の理解として正しいものはどれですか。
TIME_WAIT は先に FIN を送った側が、最後の ACK の保険と古いセグメントの毒抜きのために残る正常な状態です(第 2 章)。短命接続を秒間数百件も張り捨てれば、数十秒の滞留だけで数万件になります。害はコネクション資源(ポートなど)の消費で、根本対策は接続を毎回張り直さず使い回すこと。「異常な状態を消す」のではなく「正常な状態が大量発生する設計を直す」と読むのが正解です。
Q3. ケース: データベースへの常時接続が、深夜のバッチ処理で数時間ぶりにクエリを送った瞬間に RST で切断されました。接続確立からアイドルまでの間に異常はありません。最も疑わしい原因はどれですか。
TCP 自体はアイドルのコネクションを無期限に維持できます(両端の帳簿が残っていればよい — 第 2 章)。しかし経路上のファイアウォールや NAT は 4 つ組ごとの状態表を持っており、アイドルが続いた項目をタイムアウトで消します。その後に届いたセグメントは「所属不明」となり、破棄や RST になります。対策は TCP キープアライブやアプリ層の定期的な生存確認で「使っている」と見せ続けることです。深夜の混雑ならロスや再送が先に見えるはずです。
ケース 7-2 — 遅い・詰まる
第 3〜5 章の範囲です。「再送はどちらの型か」「min を取っているのはどの窓か」に翻訳してください。
Q4. ケース: 転送が遅いとの申告でキャプチャを見ると、同じ ack 番号の ACK が大量に並んでいました。受信側はバイト 8000 まで連続受信済みで、8001〜9000 のセグメントだけが失われ、後続(9001 以降)は届き続けています。並んでいる重複 ACK の ack 番号はいくつですか。
ack 番号は「次に欲しいバイト」で、しかも累積確認なので、穴(8001〜9000)が埋まるまでは後続が何通届いても ack = 8001 のままです(第 3 章)。つまり重複 ACK の番号は失われたセグメントの先頭バイトを直接指しています。キャプチャの重複 ACK は、どこを再送すべきかの矢印として読めるのです。
Q5. ケース: 帯域 1 Gbps(= 125 MB/秒)の専用線で海外拠点(RTT 50 ミリ秒)へファイル転送すると 4 MB/秒しか出ません。調べると、受信側の設定でウィンドウが 200 KB(200,000 バイト)に固定されていました。この設定でのスループット上限は何 MB/秒ですか。(1 MB = 100 万バイト)
上限 = ウィンドウ ÷ RTT = 200,000 バイト ÷ 0.05 秒 = 4,000,000 バイト/秒 = 4 MB/秒。観測値とぴったり一致するので、犯人はウィンドウ設定と確定できます(第 4 章)。125 MB/秒の帯域を使い切るには BDP = 125 MB/秒 × 0.05 秒 = 6.25 MB 以上のウィンドウが必要です。「回線は太いのに遅い」の第一容疑者は、いつもウィンドウ ÷ RTT です。
Q6. ケース: ログ収集サーバへの転送が周期的に止まります。キャプチャには受信側からのウィンドウ 0 の広告と、送信側からの小さな問い合わせパケットの繰り返しが並んでいました。次に調べるべき場所はどれですか。
ゼロウィンドウ + ウィンドウプローブの組は「受信バッファが満杯で、アプリが recv で読み出すのを全員が待っている」という状態の教科書的なキャプチャです(第 4 章)。ネットワークは正しくブレーキを伝えているだけで、犯人は受信側アプリの処理詰まり(ディスク書き込みの遅さ、GC、ロックなど)。ロスや輻輳なら再送や重複 ACK が見えるはずで、ウィンドウ 0 とは別の景色になります。
ケース 7-3 — データがおかしい
第 6 章の範囲です。「TCP の約束の外で何を仮定してしまったか」を探してください。
Q7. ケース: ソケットから受信した JSON を parse するサービスが、本番でだけ月に数回 parse エラーを出します。エラー時のデータを見ると、JSON の途中で切れているものと、2 つの JSON が連結しているものの両方がありました。正しい対処はどれですか。
途中で切れる(分割)と連結(結合)が両方出ているのは、「1 recv = 1 メッセージ」前提の典型症状です(第 6 章)。TCP はバイト列と順序を完全に守っており、ネットワークにもプロトコルにも異常はありません。リトライは壊れた切り出しを繰り返すだけです。境界はアプリが作る — フレーミングの導入と「貯めて・切り出して・持ち越す」ループへの修正が唯一の根治です。
Q8. ケース: 転送が数秒間完全に沈黙し、その後に同じセグメントの再送から再開する、というパターンが繰り返されていました。重複 ACK はほとんど見られません。この間、送信側の輻輳ウィンドウ(cwnd)に起きていることはどれですか。
重複 ACK がほぼ無い + 沈黙の後の再送は、高速再転送ではなくRTO 満了型の再送です(第 3 章)。このとき TCP は重症と判断し、cwnd を 1 MSS に戻してスロースタートからやり直します(第 5 章)。回復に何 RTT もかかるため、体感は「数秒固まってはノロノロ走り出す」の繰り返しに。重複 ACK 型(半減で続行)とは症状も深刻度も違う — 再送の型を見分けることが、性能問題の読みの分かれ目です。
講座の終わりに — 数字の気持ちで読む
お疲れさまでした。この講座で暗記した項目は、実はごくわずかです。代わりに持ち帰ってほしいのは、キャプチャや障害ログのどんな場面でも通用する読み方です。
- 必ず届く(届かなければ気づく) — ack は次に欲しいバイト。重複 ACK は欠けの先頭を指す矢印。再送には軽症型(重複 ACK)と重症型(RTO)がある
- 順番どおり — 累積確認と受信バッファが乱れを直す。ただし切れ目は約束の外 — 境界はアプリがフレーミングで作る
- 相手とネットワークに合わせる — 上限は min(rwnd, cwnd) ÷ RTT。ゼロウィンドウは相手の詰まり、cwnd の崖は経路の混雑
「なんとなくタイムアウト、とりあえず再起動」で止まっていた調査が、どの約束が・どのパケットで・どう破られかけたかを指させる調査に変わっていれば、この講座の目標は達成です。次にキャプチャを開くとき、seq と ack と win の列が、きっと文章のように読めるはずです。