TCP/IP とは — 4 階層モデルと TCP / UDP の違い のサムネイル

TCP/IP とは — 4 階層モデルと TCP / UDP の違い

⏱ 約 14 分 view 160 like 0 LOG_DATE:2026-05-09
目次 / TOC

TCP/IP は「インターネットを動かしているプロトコル一式」であり「それを 4 層に整理した参照モデル」でもある。本稿では 4 層モデルの俯瞰 から入り、本記事の中心 — TCP が IP のベストエフォート配送の上にどうやって信頼性を作っているか — を、3-way handshake / シーケンス番号 / 受信窓 / 輻輳制御 / 接続状態 という内部メカニズムの順に追う。最後は sstcpdump で実際の挙動を見るところまで通す。

▸ ネットワーク初学者へ — まずこの 3 つだけ

難しく見えても本質は次の 3 つだけ。(1) TCP/IP は 「インターネットを動かしている運び屋の規約一式」。住所を扱う IP と、確実に届ける TCP の 2 つが主役。(2) 機能を 「アプリ / 配送 (TCP) / 住所 (IP) / 物理線」の 4 層に分けて、各層が自分の仕事だけに集中する。(3) 本記事の主役は TCP で、その正体は 「届くか分からない郵便 (IP) を、絶対届く書留に変える工夫の集まり」。— ここを土台に各章を順に開いていけばいい。

01

4 層モデルの俯瞰 #

TCP/IP モデルは通信を 4 つの層 に分けて考える。OSI の 7 層と違い、L5/L6 を独立に切らず Application 層に吸収しているのが特徴。

▸ かみ砕いて言うと — 4 層は「引っ越し屋の役割分担」

4 層は 引っ越し屋の役割分担と同じ構造。L4 Application = 「荷物の中身」を決める依頼主 (本 / 食器 / 服)、L3 Transport (TCP) = 「壊れないように」運送会社が梱包・追跡 (届け漏れがあれば再送)、L2 Internet (IP) = 「○○県○○市まで」と住所を書き、L1 Link = 「実際に運ぶトラック」。各層は「自分の仕事だけ」をやって、隣の層の都合を気にしないのがミソ。OSI 7 層モデル (関連記事) と同じ思想だが、TCP/IP は L5/L6 を Application に統合してより実用寄りにしたバージョン、と覚えればよい。

名称 役割 代表プロトコル
L4 Application 「届いたバイト列の意味」を決めるルール HTTP / DNS / SSH / SMTP / TLS / gRPC
L3 Transport 「アプリ ↔ アプリ」の論理接続 — port で識別 TCP / UDP / QUIC / SCTP
L2 Internet 「ホスト ↔ ホスト」を世界規模で繋ぐ — IP で識別 IPv4 / IPv6 / ICMP / IPsec / OSPF / BGP
L1 Link 隣接ノード間の物理的な届け方 Ethernet / Wi-Fi / PPP / ARP / 光ファイバ
▸ 設計の核 — Internet 層はベストエフォート

IP 層は 届く保証も順序保証もしない。それを「必要なときだけ TCP が補い」「要らないなら UDP が薄く透過する」 — この分離が、インターネットがあらゆる用途に伸びていけた根本理由になっている。

各層の責務を一文ずつ言い直すと:

  • Application — HTTP のリクエスト/レスポンス, DNS のクエリ/応答 のような「バイト列の意味」のルール
  • Transport — どのアプリのどの接続か、必要なら信頼性や暗号化セッションを提供
  • Internet — この IP のホストへ世界の何処からでも届ける (ベストエフォート)
  • Link — 今この瞬間、目の前のケーブル/電波で隣のノードへ届ける
02

TCP が IP の上に作る「4 つの保証」 #

IP は「封筒に住所を書いて投函するだけ」のサービスで、届く保証も順序保証も重複しない保証もしない。それでもインターネット上のほとんどのアプリが「投げたデータは順番通りに、欠けず、重複なく届く」と仮定して書ける理由は、間に TCP が入って IP の不確かさを覆い隠しているから。

TCP が提供するのは次の 4 つの保証:

  1. コネクション — ハンドシェイクで「これから話します」を握る
  2. 順序保証 + 重複排除 + 損失回復 — シーケンス番号 + ACK + 再送
  3. フロー制御 — 受信側が「これ以上送るな」と窓で伝える
  4. 輻輳制御 — 経路の混雑を推定して送信ペースを自動調整
▸ かみ砕いて言うと — TCP は「郵便を書留に変える上着」

IP 単体は 「投函した普通郵便」のようなもので、配達員が落としたり、順番が前後したり、たまに 2 通届くこともある。TCP はその IP の上に羽織る 「書留サービス」(1) 受領印 (ACK) で「○通目までは確実に届いた」を確認し、(2) 通し番号 (シーケンス番号) で順番を保ち、(3) 相手の処理能力を見て送るペースを抑え (フロー制御)、(4) 道が混んでいたら自動で送る量を絞る (輻輳制御)。これだけで「投げたデータは順番通りに、欠けず、重複なく届く」が成立する。Web も SSH もメールも、全部この上に乗っかっている。

03

3-way handshake と 4-way teardown #

TCP の最大の特徴は コネクション指向。データ送信前に、両端で「これから話します」「いいですよ」という合意を 3 メッセージで作る。

▸ かみ砕いて言うと — 3-way handshake は「電話の挨拶」

電話で本題に入る前に 「もしもし」「はい○○です」「今お話しいいですか?」と 3 往復するのと同じ。SYN = 「もしもし、繋がる?」、SYN+ACK = 「はい繋がりました、そちらは?」、ACK = 「では話しますね」。これで 双方の電波 (シーケンス番号) が問題なく届いていることを確認してから本題に入る。TIME_WAIT電話を切った直後にすぐ同じ番号にかけ直さないマナーのようなもので、前の通話の残響が次の通話に混ざらないようにわざと 2 MSL (約 60 秒) 待つ仕組み。

1. SYN (Client → Server)
クライアントが seq=x を選んで送出。状態は SYN_SENT へ。
2. SYN+ACK (Server → Client)
サーバが自分の seq=y を返しつつ ack=x+1 で確認。状態は SYN_RECV
3. ACK (Client → Server)
クライアントが ack=y+1 を返す。両側 ESTABLISHED — application data が流せる。

3-way handshake の本質は 「双方向のシーケンス番号を相手に確認させる」 こと。各方向で 32-bit のシーケンス番号を独立に持ち、SYN で「自分はこの番号から始めます」を宣言、ACK で「+1 から待ってる」と返す。両方向で同じやりとりを行う = 3 メッセージ。

4-way teardown と TIME_WAIT #

TCP は片方向ずつ独立に close できる設計のため、終了は 4 段階になる。

1. Client → FIN
「自分はもう送らないが、まだ受け取れる」。状態は FIN_WAIT_1
2. Server → ACK
サーバは CLOSE_WAIT、クライアントは FIN_WAIT_2
3. Server → FIN
サーバアプリも close() を呼んだ時点で送る。
4. Client → ACK + TIME_WAIT
2 MSL (≈ 60 秒) 待機して、遅延した古いセグメントが新しいコネクションに紛れ込むのを防ぐ。
▸ ポート枯渇の正体

短命なセッションを大量に張ると TIME_WAIT が積み上がり「ポート枯渇」につながる。HTTP/1.1 のキープアライブ無し時代の Web サーバで頻発した古典問題。CLOSE_WAIT が長時間残るのはアプリの close() 忘れのサインで、アプリ修正以外で根治しない。

04

シーケンス番号と ACK — 順序・損失・重複の解決 #

TCP ヘッダで一番大事な 2 フィールドは シーケンス番号 (32-bit)ACK 番号 (32-bit)

  • シーケンス番号 — このセグメントの最初のバイトはコネクション上で何バイト目か
  • ACK 番号 — 次に受け取りたいバイトの位置 (= 「ここまで来ました」)

例: サーバが「seq=1000 の 500 byte」を送ったら、クライアントは「ack=1500」を返す。これだけで次の 3 つが解決する。

シーケンス番号と ACK で実現される 3 機能
# 損失検出 ACK が来ない → RTO (Retransmission Timeout) 経過 → 再送 # 順序復元 順番通りでなく届いても、seq で並べ直してアプリに渡す # 重複排除 同じ seq のセグメントは捨てる
▸ SACK で「穴」を相手に伝える

実用的には SACK (Selective ACK, RFC 2018) を使って「1000-1500 と 2500-3000 は届いた / 1500-2500 は未着」のように穴 (hole) を相手に伝え、必要分だけ再送できる仕組みが標準で動いている。

05

フロー制御 — 受信窓 (rwnd) #

送信側が受信側の処理能力を超えないようにする仕組み。受信側は ACK のたびに「今ここから何 byte 受け取れる」(= 受信窓, Window Size) をヘッダに乗せて伝え、送信側は「未 ACK のデータ量」が受信窓を超えないように送る。

▸ かみ砕いて言うと — フロー制御は「グラスの大きさ」

居酒屋でビールを注ぐとき、相手のグラスが小さければゆっくり、大きければ一気に注げる。フロー制御の 受信窓 (rwnd) はまさに 「あなたが今受け取れるグラスの大きさ」を相手に伝える数字。送信側はその窓を超えないように送るだけ。「ダウンロードが頭打ち」の正体は、多くの場合 受信側のグラスがそもそも小さすぎる (= カーネルのバッファ不足) こと。経路は太いのに窓が広がりきらないので、太い水道に細いストローを刺している状態になる。

TCP ヘッダの Window Size は 16-bit (最大 65,535) だが、現代の高速回線ではこれでは足りない。Window Scale option (RFC 7323) で窓を最大 14 bit 左シフトし、最大 1 GB まで拡張できる。

▸ 「ダウンロードが 80 MB/s で頭打ち」の原因

多くの場合受信側カーネルのバッファ不足で受信窓が広がりきれていないのが正体。net.ipv4.tcp_rmem の調整やアプリの SO_RCVBUF で解消することがある。

06

輻輳制御 — 経路の混雑を推定する #

フロー制御 (rwnd) はエンドホスト同士の都合を見ているだけ。途中経路の混雑を扱うのが 輻輳制御 (congestion control)。送信側は 輻輳窓 (cwnd) という別の窓も持ち、実際に送れる量 = min(rwnd, cwnd) で送信を絞る。

▸ かみ砕いて言うと — 輻輳制御は「高速道路の合流」

フロー制御は 「あなたと私の間」の話だが、輻輳制御は 「その間にある高速道路の混み具合」を直接見ずに推定する話。みんなが好き勝手にアクセルを踏むと渋滞して全員が遅くなるので、少しずつ速度を上げ、混雑のサイン (パケロス) が見えたら一気にペースを落とす — Slow Start と Congestion Avoidance はこれ。BBR は損失を見ずに「帯域 × 往復時間」を直接測って速度を決める新世代で、Wi-Fi のように途中で再送が多い経路で特に効く。YouTube が遠い回線でも止まりにくいのは、裏で BBR が頑張っているおかげ。

アルゴリズム 仕組み (要点) 採用状況
Reno (1990) 損失=輻輳と仮定 → cwnd を半減して再開 古い標準
CUBIC (2008) 3 次曲線で cwnd を回復、高 BDP に強い Linux のデフォルト (2.6.19〜)
BBR (2016) 損失でなく帯域 × RTT の推定を直接モデル化 Google (YouTube, GCP) で広く採用

Slow Start で cwnd を倍々に増やし、しきい値で Congestion Avoidance に移行、損失で減らす — という指数→線形→減速の 3 段サイクルが古典的だが、BBR は「損失=ペース下げ」の仮定自体を捨てている。Wi-Fi で再送の多い経路、衛星のような長 RTT、データセンター内の超低 RTT など状況ごとに性能差が大きく出るため、最近の OS では sysctl で切替可能。

輻輳制御アルゴリズムの切替 (Linux)
$ sysctl net.ipv4.tcp_congestion_control net.ipv4.tcp_congestion_control = cubic $ sysctl -w net.ipv4.tcp_congestion_control=bbr $ sysctl net.ipv4.tcp_available_congestion_control net.ipv4.tcp_available_congestion_control = reno cubic bbr
07

接続状態 — 運用で気にする 6 つ #

TCP の状態遷移は約 11 状態あるが、運用で気にすべき状態は限られている。

状態 意味 気にすべき場面
LISTEN サーバがアクセプト待ち サービスが上がっている確認
ESTABLISHED データ転送可能 アクティブな接続。多すぎないか
SYN_SENT / SYN_RECV ハンドシェイク途中 SYN_RECV 大量 = SYN flood の兆候
FIN_WAIT_1 / FIN_WAIT_2 自分が FIN を送った後 通常すぐ消える
CLOSE_WAIT 相手の FIN を受けたが自分が close 未呼出 長時間残る = アプリのバグ
TIME_WAIT active close 側 / 2 MSL 待機 大量に出ても通常は正常
▸ アンチパターン: tcp_tw_recycle

TIME_WAIT の大量発生は正常で、無理に潰すための tcp_tw_recycle (Linux 4.12 で削除) はかつてのアンチパターンとして覚えておくと安全。NAT 配下のクライアントを壊すことが知られていた。

08

UDP — 信頼性が「邪魔」なとき #

TCP のすべての仕組みは「届く / 順序 / 重複なし」を作るためのコストを払っている。リアルタイム音声・映像・ゲームのように「古いデータが順番待ちで遅れて届くより、欠けたまま新しいフレームを表示したい」用途では、TCP の保証がむしろ邪魔になる。

▸ かみ砕いて言うと — UDP は「ハガキ」

TCP が 「書留 (受領印・通し番号・再送つき)」なら、UDP は 「ただのハガキ」。投函したら届いたかどうか分からないし、3 枚出したら順番が入れ替わって着くかもしれない。代わりに 軽い・速い・準備ゼロで投げられる音声通話 / ゲーム / DNS のような「今届かないなら捨てて新しいの寄こせ」が正解の用途では、書留の手間がかえって邪魔。だから UDP のほうが嬉しい。「TCP は正確 / UDP は速い」という単純な対比は今や古い — 次章 QUIC で詳しく見る。

UDP (User Datagram Protocol, RFC 768) は TCP と対極の設計:

  • コネクションレス — ハンドシェイクなし、いきなり投げる
  • ヘッダは 8 byte のみ (送信元 port / 宛先 port / 長さ / チェックサム)
  • 再送なし / 順序保証なし / フロー制御なし / 輻輳制御なし
  • 必要な保証はアプリが自分で作る

UDP が選ばれる代表例:

  • DNS クエリ — 1 往復で済むのに TCP のハンドシェイクは過剰
  • VoIP / RTP / WebRTC — 0.5 秒前の音声を再送してもらっても意味がない
  • ゲーム — 古い座標より新しい入力を優先したい
  • DHCP / NTP / TFTP — ブートストラップ用途
09

QUIC — UDP の上に TCP+TLS+HTTP を建て直す #

TCP は信頼性・輻輳制御を OS カーネルに持っているプロトコル。これは長らく利点だったが、進化が遅い (新アルゴリズム投入に OS 更新が要る) という弱点になった。TCP と TLS のハンドシェイクが直列に積み重なる (TCP 1 RTT + TLS 1 RTT = 2 RTT) のも、現代の Web には重い。

▸ かみ砕いて言うと — QUIC は「TCP + TLS + HTTP を 1 個に詰めた箱」

従来は TCP の挨拶 → TLS の暗号鍵交換 → HTTP のリクエストと 3 段階の握手が積み上がって遅かった。QUIC は これを 1 つに統合して初接続でも 1 往復、再接続なら 0 往復で本文を流せる仕組み。さらに UDP の上にユーザ空間で作ったので 新しい改良を OS 更新を待たずに投入できる。HTTP/3 はこの上に乗る。「UDP は信頼性なし」という単純な対比は QUIC を境に古い理解になった、というのが現代の景色。

QUIC (RFC 9000, 2021) はこれをユーザ空間で UDP の上に作り直したプロトコル:

  • TCP+TLS+HTTP を 1 つに統合 — TLS 1.3 組込み、初接続でも 1 RTT、再接続なら 0 RTT
  • マルチストリーム — 1 コネクション内に独立ストリームを多重化、TCP の head-of-line blocking を解消
  • コネクション ID — IP/port が変わっても (Wi-Fi → 4G ハンドオフ) 同じコネクションで継続
  • アプリ空間実装 — 新しい輻輳制御アルゴリズムの投入が早い

HTTP/3 は「HTTP over QUIC」で、Google / Cloudflare / Meta / Akamai が広く採用。2026 年時点で新インフラの標準的な選択肢になりつつある。「UDP は信頼性なし」という単純な対比は、QUIC を境に古い理解になった。

10

パケットを実際に見る #

ここまでの仕組みは tcpdumpss でその場で観察できる。

▸ はじめての人向け — まず叩く 1 本

多数のコマンドに圧倒される前に、初手はこれだけでいい:
ss -tan | awk 'NR>1 {s[$1]++} END {for(k in s) print k, s[k]}'
これで 「今どの状態の接続が何本あるか」が一発で見える。ESTAB が多すぎる → 接続を持ちっぱなしのバグ疑いCLOSE-WAIT が常時残る → アプリの close() 忘れSYN-RECV が大量 → SYN flood の兆候。状態の内訳だけで運用のほとんどの異常は当たりが付く。深掘りしたくなったら ss -tin で RTT / cwnd / 再送数まで覗ける。

tcpdump で 3-way handshake / FIN だけを見る
$ sudo tcpdump -nn -i any 'tcp port 443 and (tcp[tcpflags] & (tcp-syn|tcp-fin) != 0)' 12:34:56.123 IP 10.0.0.5.50234 > 93.184.216.34.443: Flags [S], seq 100, ... 12:34:56.145 IP 93.184.216.34.443 > 10.0.0.5.50234: Flags [S.], seq 200, ack 101, ... 12:34:56.146 IP 10.0.0.5.50234 > 93.184.216.34.443: Flags [.], ack 201, ...
ss で状態の内訳と接続数を見る
# 状態別カウント — 運用ダッシュボードに置きたい数字 $ ss -tan | awk 'NR>1 {s[$1]++} END {for(k in s) print k, s[k]}' ESTAB 142 TIME-WAIT 38 LISTEN 12 CLOSE-WAIT 2 # 特定 port の ESTABLISHED 一覧 $ ss -tan state established sport = :443 # RTT / cwnd / 再送数まで含めた詳細 (Linux) $ ss -tin cubic wscale:7,7 rto:204 rtt:3.142/1.5 ato:40 mss:1448 cwnd:10 ssthresh:7 bytes_sent:8421 retrans:0/2
▸ 切り分けの起点になる出力

ss -tin に出る cwnd:N / rtt:M/V / retrans:R/T は、輻輳窓・往復遅延・再送数がそのまま見える。「Web ページが遅い」「特定サーバ間だけ転送が落ち込む」の切り分けは、ほぼ常にここに戻ってくる。

まとめ #

  • TCP/IP は「インターネットを動かしている運び屋」で、4 層モデルの中心が Transport (TCP)Internet (IP)
  • TCP の本質は「ベストエフォートの IP の上で信頼性を作る一連の工夫」 = ハンドシェイク + シーケンス番号 + 受信窓 + 輻輳窓
  • UDP は「工夫を全部捨てた素朴さ」、QUIC は「ユーザ空間で再設計したアジリティ」をそれぞれ別方向で示す
  • 仕組みを押さえれば ss -tin の読み方も HTTP/3 採用判断も輻輳制御選びも自分の軸で扱える
𝕏 ポスト B! はてブ