メインコンテンツへスキップ
友田 陽大
TCP/IP・ネットワーク
TCP/IP
TCP
UDP
ネットワーク
アーキテクチャ設計
パフォーマンス

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

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

公開日
読了時間
11分
著者
友田 陽大
シェア

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

この記事は、TCP と UDP の違いを IETF の一次情報で正確に押さえ、「どちらを、なぜ選ぶか」を意思決定フローとして使える形にまとめます。仕組みの詳細はTCP の仕組み、全体像はTCP/IP 完全ガイドを参照してください。

この記事のルール:規定は TCP = RFC 9293(2022年8月)UDP = RFC 768(1980年)QUIC = RFC 9000(2021年)HTTP/3 = RFC 9114(2022年) に基づきます。コードは Node.js 標準ライブラリで動く形に整えています。最新仕様は 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で後続の新しいフレームまで足止めする——これは音切れ・遅延として体感品質を悪化させます。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. 意思決定フロー——実際にどう選ぶか

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

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:接続を張り、境界は自前で区切る

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:接続なし・境界つき・届く保証なし

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 を選ぶ判断につながる)。

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 の仕組みで見た TCP の構造的弱点——ヘッドオブラインブロッキング確立RTTの固定費——を克服するために、IETF は Transport 層を作り直しました。それが QUICRFC 9000)です。

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

HTTP/3RFC 9114)はこの 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 全体像 / TCP の内部)を踏まえて選べば、後から効いてきます。


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

友田

友田 陽大

経済産業大臣賞 受賞プロダクト開発者。TypeScript + Python + AWS で、SaaS・業界DX・ 実用レベルの生成AI(RAG)を、要件定義からインフラ・運用まで一人で完遂します。

この記事の実装を、案件として承ります

ネットワーク/低レイヤ起因の本番障害の調査・信頼性設計を承ります

「ECONNRESET が止まらない」「TIME_WAIT でポートが枯れる」「再送が多くレイテンシが不安定」「決済・在庫で二重処理が怖い」「TCP/UDP/QUIC のどれを選ぶべきか」——こうしたTCP/IP・低レイヤ起因の課題を、ss/tcpdump での観測から原因を特定し、コネクションプール・タイムアウトバジェット・冪等性で根治します。モバイル回線のタイムアウトと再送を前提に冪等性で本番二重課金0件を達成した決済信頼性レイヤーの知見で、落ちない・追える・正しいバックエンドを設計します。

プロジェクト単位(請負)・技術顧問のどちらにも対応可能です。まずは30分の無料技術相談から。

あわせて読みたい