前章まで、私たちは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つの仕組みの組み合わせだ。
- シーケンス番号: 各バイトに通し番号を振る → 受信側は順序を復元でき、重複も検出できる
- ACK(確認応答): 「ここまで受け取った」と受信側が返す → 送信側は到達を確認できる
- 再送(タイムアウト): 一定時間ACKが来なければ、届かなかったとみなして再送する
送信側 受信側
│── 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は、パケットロスを「道が混んでいるサイン」と解釈し、送信量を自制する。
代表的なアルゴリズムはこう振る舞う。
- スロースタート: 最初は少量から送り始め、うまく届くたびに送信量を指数関数的に増やす(送っていい量 = 輻輳ウィンドウを倍々に)
- ロスを検知したら急減速: パケットロスが起きたら、送信量を大きく減らし、慎重に再増加する
この「様子を見ながら増やし、混んだら引く」振る舞いのおかげで、無数の通信が一つのインターネットを譲り合って共有できる。道路の合流で譲り合うのと同じ、暗黙の交通ルールだ。「なぜ通信速度が一定でなく変動するのか」の一因が、この動的な自制にある。
まとめ — 「おまじない」の消し方
- パケット交換という頑健な設計:データを小片に分けバラバラに送る。1経路が切れても届く
- 階層モデルで分割統治:アプリ/TCP/IP/リンクが各自の仕事に集中。IPは経路、TCPは信頼性を担う
- 3 wayハンドシェイク:双方の送受信能力を相互確認してから通信を始める
- 信頼性の正体:順序番号+ACK+再送で、信頼できない経路の上に信頼できる通信を作る
- 輻輳制御:ロスを「混雑サイン」と読み、送信量を自制して、皆でインターネットを共有する
「なぜバラバラに送られたデータが正しく届くのか」「なぜ通信速度は変動するのか」——その答えは、信頼できない道の上に信頼を築く、TCPの仕組みにあった。
だが、この通信はまだ丸見えだ。経路の途中で誰かが覗けば、内容も、パスワードも読めてしまう。次章では、この通信に鍵をかける技術——TLS 1.3——の暗号の物理へ潜る。
「信頼できないものの上に、信頼を築く。それは通信だけの話ではない。不確かな世界で確かなものを作るという、エンジニアリングそのものの営みが、TCPという一つのプロトコルに凝縮されている。」