# TCP と UDP の違いと使い分け：RFC で理解し、QUIC/HTTP3 まで見据えて選ぶ

> TCP と UDP のどちらを使うべきかを、IETF の一次情報（RFC 9293・768）に忠実な比較と意思決定フローで解説。信頼性・順序・境界・オーバーヘッド・実装コストの観点で違いを整理し、HTTP/DB/gRPC・DNS・リアルタイム音声/ゲーム・QUIC(HTTP3) の具体例と Node.js コードで、本番の技術選定に使える判断軸を示します。

- 公開日: 2026-06-28
- 著者: 友田 陽大
- タグ: TCP/IP, TCP, UDP, ネットワーク, アーキテクチャ設計, パフォーマンス
- URL: https://tomodahinata.com/blog/tcp-vs-udp-quic-http3-difference-when-to-use-guide
- カテゴリ: TCP/IP・ネットワーク
- 総合ガイド: https://tomodahinata.com/blog/tcp-ip-protocol-suite-fundamentals-complete-guide

## 要点

- TCP（RFC 9293）は信頼性・順序・フロー/輻輳制御つきのバイトストリーム。UDP（RFC 768）は『多重化とチェックサムだけ』のデータグラム。配送も順序も重複排除も保証しない
- 選択の本質は『信頼性をネットワークに作らせるか、アプリで作るか』。確実な配送・順序が要るなら TCP、低遅延で多少の損失より鮮度が大事なら UDP——というのが一次近似
- UDP は境界(メッセージ単位)を保つがロス・順序入替・重複が起こる。リアルタイム性が最優先（音声/映像/ゲーム/DNS）の領域で輝く。失われた古いデータは『待って再送』より『捨てて次』が正しい
- TCP の HoLB と確立RTT を、UDP の上に信頼性・暗号・多重化を再実装して克服したのが QUIC(RFC 9000)/HTTP3(RFC 9114)。『UDP だが信頼性あり』という第三の選択肢が実用化した
- 迷ったら TCP（または HTTPS/gRPC）が既定。UDP は『鮮度 > 完全性』『独自の信頼性を設計できる』ときの最適化。生UDPで信頼性を自作するのは茨の道——QUIC に乗るのが現実解

---

「ここは TCP？ それとも UDP？」——API を1本足すたびに毎回考える問いではありません。ほとんどの Web/API は HTTP（＝TCP）でよく、深く悩む必要はない。けれど**リアルタイム音声を載せる、ゲームの同期を作る、DNS を叩く、HTTP/3 に乗るべきか判断する**——そういう場面で「なんとなく TCP」を選ぶと、レイテンシで詰みます。逆に「速そうだから UDP」で信頼性を自作し始めると、TCP を劣化再実装する泥沼にはまります。

この記事は、TCP と UDP の違いを IETF の一次情報で正確に押さえ、**「どちらを、なぜ選ぶか」を意思決定フローとして使える**形にまとめます。仕組みの詳細は[TCP の仕組み](/blog/tcp-three-way-handshake-state-transition-retransmission-congestion-control-guide)、全体像は[TCP/IP 完全ガイド](/blog/tcp-ip-protocol-suite-fundamentals-complete-guide)を参照してください。

> **この記事のルール**：規定は **TCP = [RFC 9293](https://www.rfc-editor.org/rfc/rfc9293.html)（2022年8月）**、**UDP = [RFC 768](https://www.rfc-editor.org/rfc/rfc768.txt)（1980年）**、**QUIC = [RFC 9000](https://www.rfc-editor.org/rfc/rfc9000)（2021年）**、**HTTP/3 = [RFC 9114](https://www.rfc-editor.org/rfc/rfc9114)（2022年）** に基づきます。コードは Node.js 標準ライブラリで動く形に整えています。最新仕様は [rfc-editor.org](https://www.rfc-editor.org/) で確認してください。

---

## 1. 一枚で押さえる：TCP と UDP の本質的な違い

両者の差は「機能の多寡」ではなく、**信頼性という責務を誰が引き受けるか**の設計思想の違いです。

| 観点 | TCP（RFC 9293） | UDP（RFC 768） |
| --- | --- | --- |
| 接続 | コネクション指向（3ウェイハンドシェイク） | コネクションレス（いきなり送る） |
| 配送保証 | **あり**（ACK＋再送で回復） | **なし**（投げっぱなし） |
| 順序保証 | **あり**（シーケンス番号で整列） | なし（入れ替わりうる） |
| 重複排除 | あり | なし |
| データ境界 | **保たない**（バイトストリーム） | **保つ**（1 send = 1 データグラム） |
| フロー制御 | あり（受信ウィンドウ） | なし |
| 輻輳制御 | **あり**（必須・AIMD） | なし（アプリの責任） |
| ヘッダ | 最小20バイト | **8バイト** |
| 速度特性 | 確立RTT＋HoLB の固定費 | 最小オーバーヘッド・低遅延 |
| 代表用途 | HTTP/HTTPS, DB, SSH, gRPC, メール | DNS, 音声/映像, ゲーム, QUIC, syslog |

**この表の1行に集約すると**：TCP は「正しさ」を、UDP は「速さと制御の自由」を、デフォルトで選んでいます。

### 1.1 「データ境界」の違いは見落とされがち

配送保証ばかり注目されますが、実務でバグを生むのは**境界**です。

- **TCP はバイトストリーム**：`write("AB")` と `write("CD")` が、相手では `"ABCD"` 1回で届くかもしれない。**メッセージの区切りはアプリが自前で実装**（長さプレフィックス等）する必要がある。
- **UDP はメッセージ指向**：1回の `send` が、相手では**ちょうど1回の `message`** として（届けば）境界を保って受け取れる。

「UDP の方が扱いやすい」と感じる場面があるのはこのため。ただし**届く保証がない**代償とのトレードオフです。

---

## 2. なぜ UDP を選ぶのか——「再送が害になる」領域がある

「保証がないなら TCP でいいのでは？」という直感は、ある重要な事実を見落としています。**リアルタイム領域では、失われた古いデータを再送して待つより、捨てて次に進む方が正しい**のです。

### 2.1 リアルタイム音声・映像

20ms 前の音声フレームが今になって再送されても、再生位置はとうに過ぎていて**使い道がない**。TCP は律儀に再送し、しかも[HoLB](/blog/tcp-three-way-handshake-state-transition-retransmission-congestion-control-guide)で後続の新しいフレームまで足止めする——これは**音切れ・遅延として体感品質を悪化させます**。UDP（＋アプリ層のジッタバッファや FEC）なら「落ちたフレームは諦め、最新を優先」できる。WebRTC が UDP を土台にする理由です。

### 2.2 DNS——1往復で終わる小さな問い合わせ

DNS クエリは「小さな質問→小さな答え」。ここで TCP の3ウェイハンドシェイク（1RTTの確立）を毎回払うのは割に合いません。UDP なら**1往復**で完結します（応答が大きい/切り詰められた場合は TCP にフォールバック）。**確立コストを払う価値があるほどの会話量がない**のが UDP 向きの典型です。

### 2.3 オンラインゲームの状態同期

「100ms 前のプレイヤー座標」より「最新の座標」が常に正しい。順序通りの完全な履歴は不要で、**最新性が命**。UDP に独自の軽量な信頼性（重要イベントだけ ACK する等）を載せる設計が定石です。

> **共通する判断軸**：「**鮮度（最新性）＞ 完全性（取りこぼさないこと）**」が成り立つなら UDP が候補になります。逆に「1バイトでも欠けたら困る」「順序が崩れたら破綻する」なら TCP です。

---

## 3. 意思決定フロー——実際にどう選ぶか

迷ったときに上から順に当てる、実務用のフローです。

```text
Q1. 確実な配送と順序が必須か？（1バイトの欠落・順序崩れも許されない）
    ├─ YES → TCP。さらに「アプリ意味」が要るなら HTTP/gRPC（＝TCP の上）。     → 終了
    └─ NO  → Q2 へ

Q2. 低遅延・最新性が最優先で、損失をアプリで許容/補償できるか？
    ├─ NO  → 判断保留なら TCP を既定に。 → 終了
    └─ YES → Q3 へ

Q3. 「UDP だが信頼性・暗号・多重化が欲しい」か？
    ├─ YES → QUIC（HTTP/3）に乗る。生UDPで信頼性を自作しない。            → 終了
    └─ NO  → Q4 へ

Q4. 純粋に最小オーバーヘッドの片方向/小往復通信か？（音声/映像/ゲーム/DNS/メトリクス）
    └─ YES → UDP ＋ アプリ層で必要最小限の補償（FEC・選択的ACK・ジッタバッファ）。
```

**最重要の指針**：**迷ったら TCP（または HTTPS/gRPC）が既定**です。UDP は「鮮度 > 完全性」が明確で、損失への対処を設計できるときの**最適化**であって、デフォルトではありません。そして「UDP の速さ × 信頼性」が欲しいなら、自作ではなく**QUIC に乗る**のが2026年の現実解です。

---

## 4. コードで見る差：同じ「エコー」を TCP と UDP で

抽象論を具体に落とします。同じ機能を両者で書くと、設計思想の差がそのままコードに出ます。

### 4.1 TCP：接続を張り、境界は自前で区切る

```ts
import net from "node:net";

// TCP はバイトストリーム。改行などで「メッセージ境界」を自分で復元する必要がある
const server = net.createServer((socket) => {
  let buffer = "";
  socket.setEncoding("utf8");
  socket.on("data", (chunk: string) => {
    buffer += chunk;
    let nl: number;
    // 改行区切りでフレーミング（write と data は1対1でないため必須）
    while ((nl = buffer.indexOf("\n")) >= 0) {
      const line = buffer.slice(0, nl);
      buffer = buffer.slice(nl + 1);
      socket.write(`${line}\n`); // echo
    }
  });
});
server.listen(9000);
```

### 4.2 UDP：接続なし・境界つき・届く保証なし

```ts
import dgram from "node:dgram";

// UDP は 1 send = 1 message。境界は保たれるが、順序・到達・重複は保証されない
const server = dgram.createSocket("udp4");
server.on("message", (msg: Buffer, rinfo) => {
  // フレーミング不要（メッセージ指向）。ただし「届かなかった message」は永遠に来ない
  server.send(msg, rinfo.port, rinfo.address);
});
server.bind(9001);
```

**コードに思想が出ています**：TCP は「接続管理＋フレーミング」というコストを払って完全性を得る。UDP はそれを払わない代わりに、信頼性が要るなら**自分で設計する**ことになります。

### 4.3 もし UDP で「最低限の信頼性」を足すなら

UDP で重要メッセージだけ確実に届けたい場合の最小設計（**これがいかに面倒かを知ることが、QUIC を選ぶ判断につながる**）。

```ts
import dgram from "node:dgram";

/** 重要メッセージにシーケンス番号を付け、ACK が来るまで指数バックオフで再送する最小実装 */
class ReliableUdpSender {
  private seq = 0;
  private readonly pending = new Map<number, NodeJS.Timeout>();

  constructor(
    private readonly socket: dgram.Socket,
    private readonly port: number,
    private readonly host: string,
  ) {
    // 受信側からの ACK(seq) を受けて、対応する再送タイマーを止める
    socket.on("message", (msg) => {
      if (msg[0] === 0x06 /* ACK */) this.clear(msg.readUInt32BE(1));
    });
  }

  send(payload: Buffer, attempt = 0): void {
    const seq = attempt === 0 ? this.seq++ : this.lastSeq;
    this.lastSeq = seq;
    const frame = Buffer.concat([Buffer.from([0x01]), u32(seq), payload]);
    this.socket.send(frame, this.port, this.host);
    // ACK が来なければ指数バックオフで再送（=TCP の RTO/再送を手で再発明している）
    const timer = setTimeout(() => this.send(payload, attempt + 1), 200 * 2 ** attempt);
    this.pending.set(seq, timer);
  }

  private lastSeq = 0;
  private clear(seq: number): void {
    const t = this.pending.get(seq);
    if (t) clearTimeout(t), this.pending.delete(seq);
  }
}
const u32 = (n: number): Buffer => { const b = Buffer.alloc(4); b.writeUInt32BE(n); return b; };
```

これは**TCP の再送機構の劣化版を手で再発明している**にすぎません。ここに順序整列・輻輳制御・フロー制御・暗号化まで足すと、結局 TCP か QUIC を作ることになります。**だから「UDP で信頼性が欲しい」の答えは、ほぼ常に QUIC です。**

---

## 5. 第三の選択肢：QUIC / HTTP3——「UDP だが信頼性あり」

[TCP の仕組み](/blog/tcp-three-way-handshake-state-transition-retransmission-congestion-control-guide)で見た TCP の構造的弱点——**ヘッドオブラインブロッキング**と**確立RTTの固定費**——を克服するために、IETF は Transport 層を作り直しました。それが **QUIC**（[RFC 9000](https://www.rfc-editor.org/rfc/rfc9000)）です。

- **UDP の上に実装**：OS のTCPスタックやミドルボックスの制約を回避し、ユーザー空間で進化できる。
- **ストリーム独立の信頼性**：複数ストリームを多重化し、**1ストリームのロスが他を止めない**（TCPのHoLBを解消）。
- **暗号化が前提（TLS 1.3 統合）**：ハンドシェイクと暗号鍵交換を融合し、**0〜1RTT**で接続確立。
- **コネクションマイグレーション**：IPが変わっても（Wi-Fi↔モバイル）接続を維持できる。

**HTTP/3**（[RFC 9114](https://www.rfc-editor.org/rfc/rfc9114)）はこの QUIC を土台にした HTTP です。つまり「HTTP/3 にする」とは、**Transport を TCP から QUIC(UDP) へ載せ替える**こと。アプリのコードはほぼ変えずに、TCP の固定費と HoLB を外せる——これが QUIC の実務的な価値です。

> **選定の現実**：自前で UDP に信頼性を実装するのは（§4.3 の通り）TCP/QUIC の再発明です。「低遅延 × 信頼性 × 暗号化」が欲しいなら、**QUIC ライブラリ/HTTP3 対応に乗る**のが正解。生 UDP は、本当に最小オーバーヘッドが要る（音声/映像/ゲーム/メトリクス）か、QUIC が使えない制約下に限ります。

---

## 6. よくある誤解を正す

- **「UDP は速い」**＝半分正しい。**確立RTTとHoLBが無い分レイテンシで有利**だが、輻輳制御をしないUDPは混雑時にパケットを撒き散らし、**かえって全体を悪化させる**こともある。「速い」は「軽い」であって「常に高性能」ではない。
- **「TCP は重いから避ける」**＝多くの場合は誤り。Keep-Alive とコネクションプールで確立コストは償却でき、現代のCUBIC/BBRは高性能。**まず TCP を正しく使い切る**のが先。
- **「UDP だからファイアウォールが楽」**＝逆のことが多い。コネクションレスゆえステートフルFWやNATとの相性に注意が要る（NATマッピングのタイムアウトでキープアライブが必要等）。
- **「HTTP/3 にすれば必ず速くなる」**＝条件付き。**高ロス・高遅延・モバイル**で効果が大きい一方、低遅延な有線環境では差が小さいことも。計測して判断する。

---

## 7. まとめ：選択を一文で

> **確実な配送・順序が要るなら TCP（迷ったらこれ、または HTTPS/gRPC）。低遅延で『鮮度 > 完全性』が成り立つなら UDP。そして『UDP の速さ × 信頼性』が欲しいなら、自作せず QUIC/HTTP3 に乗る。**

- TCP＝信頼性をネットワークに作らせる。UDP＝信頼性をアプリの責任にする（or 要らない）。
- UDP の強みは低遅延・メッセージ境界・制御の自由。弱みは保証ゼロ（自作は茨の道）。
- QUIC は両者の良いとこ取りを標準化した第三の選択肢。HTTP/3 はその応用。
- **既定は TCP、UDP は最適化、QUIC は信頼性つき低遅延の現実解**。

プロトコル選択は、レイテンシ・体感品質・実装コストを同時に左右する上流の意思決定です。仕組み（[TCP/IP 全体像](/blog/tcp-ip-protocol-suite-fundamentals-complete-guide) / [TCP の内部](/blog/tcp-three-way-handshake-state-transition-retransmission-congestion-control-guide)）を踏まえて選べば、後から効いてきます。

---

私（友田 陽大）は、リアルタイム配信・決済・モバイル連携のバックエンドで、TCP/UDP/QUIC を要件から選定し、低遅延と信頼性を両立する設計を手がけています。「リアルタイム通信の遅延・音切れ」「HTTP/3 に移行すべきか」「UDP で独自プロトコルを作りたいが信頼性が怖い」——こうしたプロトコル選定と実装の課題を、計測と一次情報に基づいて一緒に解きほぐします。お気軽にご相談ください。
