目次
「TCPは信頼性が高く、UDPは速い」という一言は多くの人が知っています。しかし、なぜそう言えるのか、自分が作るアプリやインフラでどちらを選ぶべきか、UDPを選んだときに何を自分で面倒みなければならないかまで答えられる人は多くありません。クラウドのセキュリティグループでUDPの穴を開け忘れて「TCPは通るのにUDPだけ通らない」と半日悩んだ経験がある方もいるでしょう。
この記事では、TCPとUDPが提供する機能の違いを「何をしてくれて、何をしてくれないか」という観点で整理し、DNS・動画配信・ゲーム・VoIP・QUICといった具体的な用途でなぜその選択になるのかを説明します。さらに、ファイアウォールでUDPが通らない問題の切り分けと、アプリ開発者がプロトコルを扱う際に意識すべき設計上のポイントまでを扱います。TCP/IPの階層構造そのものはエンジニアのためのネットワーク基礎を参照してください。
TCPとUDPの根本的な違い:何を保証し、何を保証しないか
TCPは「順序どおり・欠けなく・相手に合わせて」届ける
TCPはコネクション型のプロトコルです。通信前に3ウェイハンドシェイクで接続を確立し、送ったデータにはすべてシーケンス番号を付け、相手からのACK(確認応答)が返ってこなければ再送します。受信側はシーケンス番号をもとにデータを並べ直すため、アプリケーションには「送った順序どおりに、欠けのないバイト列」が渡されます。加えて、相手の受信バッファに合わせて送信量を調整するフロー制御と、ネットワークの混雑を検知して送信速度を落とす輻輳制御を備えています。接続確立や切断時の状態遷移はTCPの3ウェイハンドシェイクとTIME_WAITで詳しく扱っています。
UDPは「宛先に投げるだけ」で、あとはアプリ任せ
UDPはコネクションレスです。接続確立の手続きはなく、データグラム(1つの塊)に宛先ポートを付けて送信するだけで、届いたかどうかの確認も、順序の保証も、再送も、輻輳制御もありません。届かなければ黙って消えますし、後から送ったものが先に届くこともあります。その代わりヘッダーはわずか8バイト(TCPはオプションなしで20バイト、通常はタイムスタンプ等が付いて32バイト前後)で、送信側は相手の状態を気にせず好きなタイミングで送れます。この「余計なことをしない」性質が、遅延を最優先する用途で武器になります。
機能比較表
| 項目 | TCP | UDP |
|---|---|---|
| 接続 | コネクション型(ハンドシェイクあり) | コネクションレス |
| 到達保証・再送 | あり | なし(アプリで実装) |
| 順序保証 | あり | なし |
| フロー制御・輻輳制御 | あり | なし |
| データの単位 | バイトストリーム(境界なし) | データグラム(境界あり) |
| ヘッダサイズ | 20〜60バイト | 8バイト |
| ブロードキャスト/マルチキャスト | 不可 | 可能 |
| 典型用途 | HTTP/1.1・HTTP/2、SSH、メール、DB接続 | DNS、NTP、DHCP、VoIP、動画、ゲーム、QUIC |
「データの単位」の違いは開発者が特に意識すべき点です。TCPはバイトストリームなので、send()を2回呼んでも受信側で1回のrecv()にまとめて届くことがあり、メッセージの区切りはアプリ側で長さプレフィックスや区切り文字を使って定義する必要があります。UDPは1回の送信が1つのデータグラムとして届くため、区切りは自然に保たれますが、大きすぎるとIPレベルで分割(フラグメント)されて損失率が上がります。
用途別の使い分け:なぜその選択になるのか
DNS:小さな問い合わせは往復1回で済ませたい
DNSの問い合わせは通常UDPの53番ポートを使います。名前解決は数十バイトの質問と数百バイトの回答という小さなやり取りで、TCPのハンドシェイクを毎回やる方がコストになるからです。届かなければリゾルバがタイムアウトで再送すればよく、順序も関係ありません。ただし応答が大きい場合(DNSSECの署名付き応答やゾーン転送など)はTCPに切り替わります。「DNSはUDP」と覚えるだけでなく「大きな応答ではTCP 53も必要」と知っておくと、ファイアウォールでTCP 53を閉じたことによる断続的な名前解決失敗を防げます。DNSの仕組みはDNSの仕組みをわかりやすく解説で扱っています。
動画・音声通話・ゲーム:古いデータを待つより捨てる
ビデオ通話やオンラインゲームでは、0.5秒前の音声や位置情報が遅れて届いても意味がありません。TCPだと1パケットの損失で後続がすべて再送待ちになり(HoLブロッキング)、映像がカクつくどころか止まります。UDPなら失われたフレームは飛ばして次に進めるため、多少のノイズはあっても会話は続きます。VoIPで使われるRTP、WebRTCのメディア転送、多くのゲームのネットコードがUDPを選ぶのはこの理由です。もっとも、これらは「再送しない」のではなく、「アプリの都合に合わせた再送や誤り訂正を自前で実装している」点に注意してください。
一方、YouTubeやNetflixのようなオンデマンド動画配信は、数秒先までバッファできるため、HTTP(TCPまたはQUIC)上で分割ファイルをダウンロードするHLS/DASH方式が主流です。「動画=UDP」と単純化せず、リアルタイム性の要求で判断します。
QUIC:UDPの上に「TCPの良いところ」を作り直す
HTTP/3の土台であるQUICは、UDP上で再送・順序制御・輻輳制御・暗号化をユーザー空間で再実装したプロトコルです。TCPを改良するにはOSや中継機器の更新が必要ですが、UDPなら既存のインフラをそのまま通せるため、「UDPを選んだ」というより「進化の速い土台としてUDPを使った」と言えます。ストリームごとに独立した再送を行うためTCPのHoLブロッキングが起きず、接続確立も1往復で済みます。詳しくはHTTP/2とHTTP/3(QUIC)の違いを参照してください。
ファイアウォールでUDPが通らない問題
「TCPは通るのにUDPだけ通らない」典型パターン
ステートフルファイアウォールやクラウドのセキュリティグループは、TCPの接続状態を追跡して「確立済み接続の戻りパケット」を自動で許可します。UDPには接続の概念がないため、ファイアウォールは「最近このIP・ポートの組で外向きパケットがあった」という擬似的な状態を短時間だけ保持して戻りを通します。Linuxのconntrackではこの保持時間がデフォルトで30秒(双方向の通信が確認されると180秒)と短く、無通信が続くと戻りパケットが落とされます。VPNやVoIPが「数分すると片方向だけ聞こえなくなる」現象は、この状態の消滅が原因であることが多く、対策として定期的なキープアライブパケットを送ります。
もう1つの典型は、ルールをTCPだけで書いてしまうミスです。AWSのセキュリティグループもufwも、プロトコルは明示指定なので「443を開けた」だけではTCP 443しか通りません。HTTP/3やWireGuardを有効化するときはUDPのルールを別途追加する必要があります。
# ufw: UDPポートを明示的に許可(TCPとは別ルール)
sudo ufw allow 443/udp
sudo ufw allow 51820/udp # WireGuardの例
# firewalld
sudo firewall-cmd --permanent --add-port=443/udp
sudo firewall-cmd --reload
# サーバーがUDPを待ち受けているか(-u でUDP、-l でLISTEN)
ss -ulnp
# 疎通確認:ncでUDPパケットを投げる(応答がないプロトコルなら到達確認は難しい)
nc -u -z -v example.com 53
UDPの疎通確認はTCPより難しい点も理解しておきましょう。TCPならnc -zvでハンドシェイクの成否がわかりますが、UDPは相手が応答しなければ「届いたが無視された」のか「途中で捨てられた」のか区別できません。DNSのように必ず応答するサービスならdig @サーバーで確認できますが、それ以外は両端でtcpdumpを取って比較するのが確実です。ファイアウォールの基本操作はLinuxファイアウォール設定入門にまとめています。
アプリ開発者が意識すべき点
ソケットAPIでの違いを見てみる
言語のソケットAPIでは、TCPはSOCK_STREAM、UDPはSOCK_DGRAMとして区別されます。Pythonで最小のUDPサーバーとクライアントを書くと、TCPと比べてlistenやacceptがなく、送受信のたびに相手アドレスを扱う点が違いとして現れます。
import socket
# UDPサーバー:接続の概念がないので listen/accept は不要
srv = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
srv.bind(("0.0.0.0", 9999))
data, addr = srv.recvfrom(1400) # 1データグラムを受信。addr は送信元
srv.sendto(b"pong", addr) # 相手アドレスを毎回指定して返す
# UDPクライアント:届く保証がないので自前でタイムアウトと再試行を持つ
cli = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
cli.settimeout(1.0)
for attempt in range(3):
cli.sendto(b"ping", ("127.0.0.1", 9999))
try:
reply, _ = cli.recvfrom(1400)
break
except socket.timeout:
continue
このコードにある「タイムアウトと再試行」は、TCPならOSがやってくれていた仕事です。UDPを選ぶということは、この責任をアプリケーションが引き受けることを意味します。
設計判断のチェックリスト
- データの欠落や順序の入れ替わりが許容できるか。許容できないならTCP(またはQUIC)を選ぶ。UDPで信頼性を自作すると、結局TCPの再発明になりやすい
- 遅延の上限が厳しいか。数百ミリ秒の再送待ちが致命的ならUDPを検討する
- 1データグラムのサイズを1,200〜1,400バイト程度に収められるか。経路のMTU(一般に1,500バイト)を超えるとフラグメント化され、1片でも失われれば全体が失われる
- NATやファイアウォールを越える必要があるか。UDPは状態保持の都合でキープアライブ設計が必須になる
- 暗号化をどうするか。TCPにはTLSがあるが、UDPではDTLSやQUICなど別の仕組みが必要
実務では、Webアプリのバックエンド間通信やDB接続でUDPを直接扱う場面はほとんどありません。UDPを意識するのは、DNS・NTP・監視エージェント(StatsDなど)といった既存プロトコルの穴あけと、WebRTCやHTTP/3といった「UDPの上に信頼性が実装済みのプロトコル」を導入する場面です。自分でUDPプロトコルを設計するのは最後の手段だと考えておくと、判断を誤りにくくなります。
トラブル事例:監視メトリクスが「ときどき欠ける」
症状
アプリからStatsD形式でUDP送信していたメトリクスが、グラフ上で不規則に欠落する。アプリのログにはエラーが一切出ておらず、送信処理は正常終了している。
原因
UDPは送信が成功しても届いた保証がなく、エラーも返りません。調べると、ピーク時にアプリが1メトリクス1データグラムで毎秒数万パケットを送っており、受信側の監視エージェントのUDP受信バッファ(net.core.rmem_defaultで決まる、デフォルトは200KB強)があふれてカーネルがパケットを破棄していました。cat /proc/net/snmp | grep UdpのRcvbufErrorsが増え続けていたことで確定しました。
対処
アプリ側で複数のメトリクスを1つのデータグラム(1,400バイト以内)にまとめて送信頻度を下げ、受信側はnet.core.rmem_maxと監視エージェントの受信バッファ設定を引き上げました。「エラーが出ていないから送れている」はUDPでは成り立たないため、送信側のカウントと受信側のカウントを突き合わせる監視を追加したのが再発防止策です。
まとめ
TCPは到達・順序・輻輳制御をOSが引き受けてくれる代わりに接続確立と再送待ちのコストがあり、UDPはそれらを一切しない代わりに軽くて遅延が小さいプロトコルです。DNSやNTPのような小さな問い合わせ、音声・映像・ゲームのようなリアルタイム通信、そしてQUICのように「UDPの上で信頼性を作り直す」場面でUDPが選ばれます。ファイアウォールではTCPとUDPのルールが別であること、UDPの疎通確認は難しいこと、そしてUDPを使うなら再送・サイズ・キープアライブの責任をアプリが負うことを押さえておけば、プロトコル選択で迷う場面は大きく減るはずです。
プロトコルの選定やネットワーク構成の見直しでお困りの際は、Harmonic Societyのシステム開発・インフラ支援までお気軽にご相談ください。
Harmonic Society
この記事の内容、自社の業務でも活かせそうですか?
ローカルLLM・AI・クラウドなどの技術導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK。まずは30分の無料相談から。売り込みはしません。
関連記事
Related / 9 articles
Notes & Insights
- プログラミング
DDoS攻撃の仕組みと対策入門|レイヤー別の防御とCDN・クラウドの活用
DDoS攻撃をボリューム型・プロトコル型・アプリ層に分けて仕組みを解説し、自前サーバーで防げない理由、CloudflareやAWS Shieldの標準防御、オリジンIPの隠し方、レートリミットとBot対策、攻撃を受けたときの初動、費用が跳ね上がるDenial of Walletへの備えまでわかります。
- プログラミング
WAFとは?仕組み・導入パターン・誤検知対策|Webアプリを攻撃から守る実践ガイド
WAFがファイアウォールやIDSと何が違うのか、シグネチャとマネージドルールの仕組み、Cloudflare WAF・AWS WAF・ModSecurityの比較、フォーム送信がブロックされる誤検知の調査と例外設定、ログ監視、WAFが代替できないことまで実践的に解説します。
- プログラミング
セキュリティヘッダー入門|CSP・HSTS・X-Frame-Optionsの設定と効果を実践解説
CSP・HSTS・X-Frame-Options・X-Content-Type-Optionsなど主要セキュリティヘッダーが防ぐ攻撃と、CSPのReport-Onlyからの段階導入、nonce/hash、HSTS preloadの不可逆リスク、Nginx・Next.js・Astroでの設定例、確認方法を解説します。
- プログラミング
クラウドの通信費(Egress)入門|データ転送量課金の仕組みと転送コストを抑える設計
クラウドの「受信無料・送信有料」の原則、AZ間・リージョン間・インターネット向けの単価差、NATゲートウェイ処理料の罠、CDNで転送量を減らす方法、バックアップやログ転送の見落とし、請求書で転送料を特定する手順を解説。想定外の請求を防げます。
- プログラミング
秘密情報をGitに入れない仕組み|.gitignore・git-secrets・履歴から漏れた鍵の削除
APIキーや.envをGitにコミットしてしまう典型経路と、.gitignore・.env.exampleの運用、pre-commitでのgitleaks検知、GitHub secret scanningの活用、漏れた鍵の無効化と履歴書き換え(git filter-repo)の手順を解説。仕組みで再発を防げます。
- プログラミング
開発・ステージング・本番環境の分離設計|環境差分をなくす構成とアクセス制御
開発・ステージング・本番それぞれの目的と、構成をコードで揃える方法、環境別の設定注入、本番データを使わないテストデータ戦略、ステージングの保護(Basic認証・IP制限・noindex)、コストを抑える運用までを解説。環境差分による本番障害を防げます。
- プログラミング
ngrok・Cloudflare Tunnelでローカルを公開|Webhook開発とデモ環境の作り方
NAT内のローカル環境にStripeやLINEのWebhookを届けるトンネリングの仕組みを解説。ngrok・Cloudflare Tunnel・localtunnelの比較、固定ドメインと認証、リクエスト検査、公開時のセキュリティ、自宅サーバー公開への応用までわかります。
- プログラミング
ローカル開発環境のHTTPS化|mkcert・hostsファイル・自己署名証明書の正しい使い方
ローカル開発をHTTPS前提にすべき理由(Secure Cookie・Service Worker・OAuth)と、mkcertでローカルCAを作りhostsで独自ドメインを割り当ててVite・Next.js・Dockerで使う手順を解説。証明書警告を無視する癖の危険も理解できます。
- プログラミング
localhost・0.0.0.0・127.0.0.1の違い|ポートとUnixソケットを理解して「つながらない」を解決
localhost・127.0.0.1・0.0.0.0の意味の違い、Dockerで外から接続できない原因、host.docker.internal、ポート競合の調べ方、Unixソケットの利点と権限、1024未満ポートの制約を解説。「つながらない」を仕組みから解決できます。
Harmonic Society
「読んで終わり」にせず、自社の業務で試してみませんか?
AI・ローカルLLM・クラウドの導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK・売り込みなし。
無料・30分・オンラインOK|1営業日以内に返信します