DIVE

Part 5: ネットワーク・SRE

第 13 章

TCP/IP — バラバラの荷物が、なぜ正しく届くのか

前章まで、私たちは1台のマシンの中を旅してきた。だが現代のソフトウェアは、決して1台では完結しない。あなたが押した「送信」ボタンのデータは、地球の裏側のサーバーまで届く。

しかもその経路は、無数のルーターを経由する信頼できない道だ。途中でデータは失われ、順序が入れ替わり、時に化ける。にもかかわらず、あなたのメッセージは相手に正確に、順序通り届く。この奇跡を成立させているのがTCP/IPだ。この章で、その仕組みを解体する。


なぜ「分割」するのか — 歴史の必然

初期の通信は、電話のように専用の回線を占有する方式(回線交換)だった。通信中はその線を独占するため、無駄が多く、線が1本切れれば通信も切れた。

インターネットの祖ARPANETが選んだのは、まったく別の思想——パケット交換だ。データを小さな塊(パケット)に分割し、それぞれに宛先を書いて、バラバラに送り出す。各パケットは、その時空いている経路を自由に通る。途中のルーターが1台落ちても、別の道を通れば届く。核戦争でも生き残る通信網という冷戦下の要求が、この頑健な設計を生んだ。

1つのメッセージ「HELLO WORLD」を分割して送る:

  [HE][LL][O ][WO][RL][D]   ← パケットに分割、各々に宛先と順序番号
     │   │   │   │   │   │
     ▼   ▼   ▼   ▼   ▼   ▼
  バラバラの経路を通る(順序が入れ替わることもある)
     │   │   │   │   │   │
     ▼   ▼   ▼   ▼   ▼   ▼
  受信側で順序番号を頼りに組み立て直す → 「HELLO WORLD」

だが、この自由さには代償がある。パケットは失われ、順序が狂い、重複するかもしれない。この「信頼できない配達」を、どうやって「信頼できる通信」に変えるのか。答えが、階層設計だ。


厨房のアナロジー — 大量注文を小分けにして運ぶ

宴会場から「コース料理20人分」の大注文が入ったとしよう。一枚の巨大な皿で運ぶのは不可能だ。だから料理は一皿ずつ小分けにし、それぞれに「20番テーブル・3品目」の伝票を付けて、複数のウェイターが別々のルートで運ぶ。

途中で一皿こぼれたら(パケットロス)、その皿だけ作り直して再送する。運ぶ順序が前後しても、伝票の番号を見れば正しい順に並べ直せる。全品そろって初めて「配膳完了」だ。

私が飲食店で大人数の宴会を回したとき、まさにこの管理をしていた。料理を小分けにし、番号で追跡し、抜けがあれば作り直す。「大きな仕事を、追跡可能な小片に分けて、抜けなく届ける」——TCPがやっているのは、この配膳管理の徹底版だ。


階層モデル — 関心事を分けて統治する

TCP/IPの最大の発明は、通信を役割ごとの層(レイヤー)に分けたことだ。各層は自分の仕事だけに集中し、上下の層の詳細を知らなくてよい。第7章の「すべてはファイル」と同じ、抽象化による分割統治だ。

層役割(厨房の比喩)代表
アプリケーション層何を伝えるか(注文の中身)HTTP, DNS
トランスポート層確実に順序通り届ける(配膳管理)TCP, UDP
インターネット層宛先まで経路を選ぶ(住所と道順)IP
リンク層隣のノードへ物理的に送る(隣の部屋へ手渡し)Ethernet, Wi-Fi

送信時は各層が自分のヘッダ(宛先情報や制御情報)を上から順に付け足していく。これをカプセル化という。受信側は逆順に剥がしていく。

送信(上→下、ヘッダを付け足す = カプセル化)
  [ HTTPデータ ]
  [ TCPヘッダ | HTTPデータ ]                ← ポート番号・順序番号を付与
  [ IPヘッダ | TCPヘッダ | HTTPデータ ]       ← 送信元/宛先IPを付与
  [ Ethernetヘッダ | IP | TCP | HTTPデータ ]  ← 物理アドレス(MAC)を付与
        │
        ▼ 物理的に送信
  受信側は逆順に剥がして、最後にHTTPデータを取り出す

IP は「宛先まで届ける努力はするが、保証はしない」層だ(ベストエフォート)。順序も再送も保証しない。その上で、TCP が「信頼性」を作り出す。役割がきれいに分かれている。

IPアドレスとポート — 住所と部屋番号

IPアドレスはマシンの住所(例: 93.184.216.34)。だが1台のマシンでは複数のアプリ(Webサーバー、DB…)が動く。どのアプリ宛かを区別するのがポート番号だ。IPが「マンションの住所」、ポートが「部屋番号」にあたる。「IP:ポート」の組で、通信の相手が一意に決まる。


3 wayハンドシェイク — 通信を「始める」儀式

TCPは、いきなりデータを送らない。まず双方が「話す準備ができたか」を確認し合う。この3回のやり取りが3 wayハンドシェイクだ。

クライアント                          サーバー
    │                                  │
    │──── SYN (seq=x) ───────────────▶│  ①「話したい。私の番号はx」
    │                                  │
    │◀─── SYN+ACK (seq=y, ack=x+1) ───│  ②「OK。私の番号はy、xは受け取った」
    │                                  │
    │──── ACK (ack=y+1) ─────────────▶│  ③「yも受け取った。始めよう」
    │                                  │
    │═══════ 接続確立、データ通信開始 ═══│

なぜ3回か。双方が「送れること」と「受け取れること」を相互確認するには、最低3回のやり取りが要る。①でクライアント→サーバーの疎通確認、②でサーバー→クライアントの確認と応答、③で最終確認。ここでやり取りするシーケンス番号(seq)が、後でパケットの順序を保証する土台になる。

この儀式には往復(RTT: Round Trip Time)が必要で、遠いサーバーほど接続開始に時間がかかる。「なぜ最初のアクセスは少し待たされるのか」の一因がこれだ。第14章のTLSは、この上にさらにハンドシェイクを重ねるため、接続確立の高速化が重要テーマになる。


信頼性の正体 — 順序番号・ACK・再送

TCPが「信頼できない経路」の上で「信頼できる通信」を作る核心は、3つの仕組みの組み合わせだ。

  1. シーケンス番号: 各バイトに通し番号を振る → 受信側は順序を復元でき、重複も検出できる
  2. ACK(確認応答): 「ここまで受け取った」と受信側が返す → 送信側は到達を確認できる
  3. 再送(タイムアウト): 一定時間ACKが来なければ、届かなかったとみなして再送する
信頼性=順序番号(並べ直す)+ACK(届いたか確認)+再送(届くまで諦めない)\text{信頼性} = \text{順序番号(並べ直す)} + \text{ACK(届いたか確認)} + \text{再送(届くまで諦めない)}
送信側                         受信側
  │── seq=1 ─────────────────▶│ 受信
  │── seq=2 ────────╳ (ロス)   │
  │── seq=3 ─────────────────▶│ 受信(だがseq=2が欠けている)
  │◀──────── ACK=2 (2が欲しい) │  ← 「2をくれ」と要求
  │── seq=2 (再送) ───────────▶│ 受信 → 1,2,3 が揃った ✓

こうして、途中で失われても、順序が狂っても、最終的に送った通りのデータが相手に組み上がる。ARPANETが選んだ「信頼できないパケット交換」の弱点を、TCPがソフトウェアの仕組みで克服しているのだ。

なお、この保証をあえて捨てるのが UDP だ。順序保証も再送もしない代わりに、オーバーヘッドが小さく速い。リアルタイム性が命の動画配信・オンラインゲーム・DNSは、多少の欠けより速さを選び、UDPを使う。「信頼性」と「速度」は、ここでもトレードオフだ。


輻輳制御 — 道が混んだら自制する

もう一つ、TCPには社会的な知恵がある。輻輳制御(congestion control)だ。

全員が全力でパケットを送りつけたら、ネットワークの経路(道路)はパンクし、パケットロスが多発して全員が損をする(輻輳崩壊)。そこでTCPは、パケットロスを「道が混んでいるサイン」と解釈し、送信量を自制する。

代表的なアルゴリズムはこう振る舞う。

スループット≈輻輳ウィンドウRTT(往復時間)\text{スループット} \approx \frac{\text{輻輳ウィンドウ}}{\text{RTT(往復時間)}}

この「様子を見ながら増やし、混んだら引く」振る舞いのおかげで、無数の通信が一つのインターネットを譲り合って共有できる。道路の合流で譲り合うのと同じ、暗黙の交通ルールだ。「なぜ通信速度が一定でなく変動するのか」の一因が、この動的な自制にある。


まとめ — 「おまじない」の消し方

  1. パケット交換という頑健な設計:データを小片に分けバラバラに送る。1経路が切れても届く
  2. 階層モデルで分割統治:アプリ/TCP/IP/リンクが各自の仕事に集中。IPは経路、TCPは信頼性を担う
  3. 3 wayハンドシェイク:双方の送受信能力を相互確認してから通信を始める
  4. 信頼性の正体:順序番号+ACK+再送で、信頼できない経路の上に信頼できる通信を作る
  5. 輻輳制御:ロスを「混雑サイン」と読み、送信量を自制して、皆でインターネットを共有する

「なぜバラバラに送られたデータが正しく届くのか」「なぜ通信速度は変動するのか」——その答えは、信頼できない道の上に信頼を築く、TCPの仕組みにあった。

だが、この通信はまだ丸見えだ。経路の途中で誰かが覗けば、内容も、パスワードも読めてしまう。次章では、この通信に鍵をかける技術——TLS 1.3——の暗号の物理へ潜る。


「信頼できないものの上に、信頼を築く。それは通信だけの話ではない。不確かな世界で確かなものを作るという、エンジニアリングそのものの営みが、TCPという一つのプロトコルに凝縮されている。」