結論

UDPは、接続・再送・順序の保証をしない代わりに、8バイトのヘッダーで「1回送って終わり」にできる軽いプロトコルです。確実に届けたいならTCP、速さと軽さを優先し、欠けても次のデータで補えるならUDPを使います。

この記事でわかること

  • 同じ1行を送ったとき、TCPは8パケット・UDPは1パケットになるtcpdumpの原文
  • ip netnsで損失30%と遅延を起こし、UDPで「届かない番号」「順番の入れ替わり」が出た実物
  • DNS・QUICでUDPが選ばれる理由と、ping・tracerouteで見るICMPとUDPの関係

2026年10月29日更新|Ubuntu 24.04・Python 3.13・tcpdump 4.99・nftables 1.0.9で実行確認

TCPは「つないでから送り、届いたか確かめ、足りなければ送り直す」プロトコルで、UDPは「宛先とポートを付けて投げる」だけのプロトコルです。どちらもIPの上で動き、アプリケーションをポート番号で区別します。

flowchart TD
    D[送りたいデータ] --> U[UDP<br>1 個送って終わり]
    D --> T[TCP<br>SYN・SYN+ACK・ACK]
    U --> U2[届いたかは分からない<br>順番も保証しない]
    T --> T2[データ → ACK<br>無ければ再送]
    T2 --> T3[全部・順番どおり]

よく調べられることの答えを先にまとめます。

知りたいこと 答え 詳しい節
UDPとは 接続せず、届いたか確かめずに送るトランスポート層のプロトコル UDPとは
UDPとTCPの違い 接続・再送・順序・ヘッダーの大きさが違う UDPとTCPの違い
本当に届かないのか 損失30%の経路で20個中7個が消え、順番も入れ替わった 届かない・順番が変わる
UDPは速いのか 軽いが「必ず速い」ではない。受け取る側が遅いと手元でも半分消えた UDPは必ず速いのか
UDPが使われるところ DNS・DHCP・NTP・動画・音声・ゲーム・QUIC(HTTP/3) UDPが使われる理由
ICMPとは IPのエラーと診断を伝えるプロトコル。pingとtracerouteが使う ICMPとは
UDPとICMPの関係 UDPの宛先ポートが閉じていると、ICMPの「port unreachable」が返る ICMPとは
どちらを使うか 欠けて困るならTCP、遅れて困るならUDP 使い分け判断表

確認環境:Ubuntu 24.04.5 LTS(Linux 6.18)の上で、nc(OpenBSD netcat 1.226)・Python 3.13.18・tcpdump 4.99.4・ss(iproute2 6.1.0)を 127.0.0.1 で動かしました。損失・遅延・順番の入れ替えは、ip netns で作った3つのネットワーク名前空間(送る側 10.213.0.1、中継 10.213.0.254/10.213.1.254、受け取る側 10.213.1.2)の中だけで起こし、損失は中継の nftables 1.0.9(numgen random)、遅延と入れ替えは中継の tc(HTB)で作っています。ping は iputils 20240117、traceroute は 2.1.5、DNS の応答は dnsmasq 2.91 を名前空間の中に立てて確かめました。ホストのネットワーク設定は変えていません。UDP・TCP・ICMP・QUIC の仕様は RFC 768・9293・792・9000、Linux の ICMP の送信制限はカーネルのドキュメントで確かめています。図は mermaid 11.16.0 で描画を確かめています(確認日 2026年10月29日)。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「UDPとは|TCPとの違い・ICMPとの関係を「届かない」実物で解説」

UDPとは(User Datagram Protocol)

この節では、UDPが何をして、何をしないプロトコルなのかが分かります。

UDP(User Datagram Protocol)は、IPの上でアプリケーション同士がデータをやり取りするためのトランスポート層のプロトコルで、「データグラム」と呼ぶひとかたまりを、接続の手続きなしに送ります。 仕様は RFC 768(1980年)で、本文はわずか3ページです。IPのプロトコル番号は17、TCPは6、ICMPは1です。

UDPがすること

ポート番号で届け先のアプリケーションを区別する

長さとチェックサムで、壊れたデータグラムを捨てる

1回の送信を1つのデータグラムとして、境界を保ったまま届ける

UDPがしないこと

接続の確立(相手がいるかどうかを確かめない)

届いたかの確認と再送

順番の並べ直し・重複の除去・流量の調整

「しないこと」が多いのは欠点ではなく設計です。確認や再送を省いた分、ヘッダーが小さく、1往復の待ち時間が無く、サーバーは相手ごとの接続の状態を持たずに済みます。足りない機能は、必要なアプリケーションが自分で足します(QUICがその代表です)。TCP側の仕組みは、関連記事「TCPの落とし穴8選|TIME_WAIT・40ms遅延の正体」で扱っています。

UDPとTCPの違い(一覧表)

この節では、UDPとTCPの違いが一覧で分かります。

項目 UDP TCP
通信の形 コネクションレス(つながずに送る) コネクション型(3ウェイハンドシェイクで接続)
届いたかの確認 しない ACKで確認し、届かなければ再送
順番の保証 しない(入れ替わることがある) シーケンス番号で並べ直す
データの単位 データグラム(1回の送信=1個、境界が残る) バイトストリーム(境界は残らない)
ヘッダー 8バイト固定 20バイト以上(手元では32バイト)
流量の調整 しない(送りすぎると捨てられる) ウィンドウと輻輳制御で調整する
サーバーが持つ状態 ほぼ無し(ssでは UNCONN) 接続ごとに持つ(LISTEN・ESTAB など)
代表的な用途 DNS・DHCP・NTP・音声・動画・ゲーム・QUIC HTTP/1.1・HTTP/2・SSH・メール・ファイル転送

手紙にたとえると

UDPはがきをポストに入れる。届いたかは分からないし、何枚も出せば順番も前後する。その代わり切手1枚で済む
↔
TCP書留で送る。受け取りの確認が返り、届かなければ出し直す。手間と時間はかかる

見分ける問い:「1枚欠けたら困るか、1枚遅れたら困るか」

7層のどこに位置するかは、関連記事「OSI参照モデルとは|7層の早見表とTCP/IP対応・パケット実例」で扱っています。

ncで見比べる:同じ1行がTCPは8パケット・UDPは1パケット

この節では、同じ「hello」を送ったときに、TCPとUDPで実際に何が流れるのかが tcpdump の原文で分かります。

127.0.0.1 で nc の待ち受けを作り、hello を1行送って tcpdump で見ました。TCPは8パケット、UDPは1パケットでした。

# TCP:待ち受けと送信
$ nc -l 127.0.0.1 45613 &
$ printf 'hello\n' | nc -N 127.0.0.1 45613
# UDP:-u を付けるだけ
$ nc -lu 127.0.0.1 45614 &
$ printf 'hello\n' | nc -u -w1 127.0.0.1 45614
$ tcpdump -i lo -nn 'port 45613'      # TCP(Flags の S=SYN . =ACK P=PUSH F=FIN)
127.0.0.1.43796 > 127.0.0.1.45613: Flags [S], seq 2863601208, win 65495, options [mss 65495,sackOK,TS val 3481000855 ecr 0,nop,wscale 10], length 0
127.0.0.1.45613 > 127.0.0.1.43796: Flags [S.], seq 2157785620, ack 2863601209, win 65483, options [...], length 0
127.0.0.1.43796 > 127.0.0.1.45613: Flags [.], ack 1, win 64, options [...], length 0
127.0.0.1.43796 > 127.0.0.1.45613: Flags [P.], seq 1:7, ack 1, win 64, options [...], length 6
127.0.0.1.45613 > 127.0.0.1.43796: Flags [.], ack 7, win 64, options [...], length 0
127.0.0.1.43796 > 127.0.0.1.45613: Flags [F.], seq 7, ack 1, win 64, options [...], length 0
127.0.0.1.45613 > 127.0.0.1.43796: Flags [F.], seq 1, ack 8, win 64, options [...], length 0
127.0.0.1.43796 > 127.0.0.1.45613: Flags [.], ack 2, win 64, options [...], length 0

$ tcpdump -i lo -nn 'port 45614'      # UDP
127.0.0.1.47186 > 127.0.0.1.45614: UDP, length 6

1接続に3パケット

SYN → SYN+ACK → ACK。データを1バイトも送る前に1往復が要る。

2データに2パケット

6バイト(hello と改行)を送り、受け取った側が ack 7 で「7バイト目まで受け取った」と返す。

3切断に3パケット

FIN → FIN+ACK → ACK。UDPには始まりも終わりも無く、1個のデータグラムだけが流れた。

「UDPは軽い」の中身は、この往復の差です。DNSのように問い合わせ1個・答え1個で終わる通信では、TCPだと3往復以上、UDPなら1往復で済みます。

ヘッダーの大きさ:UDPは8バイト・TCPは20バイト以上

この節では、UDPとTCPのヘッダーの中身と、同じ6バイトを送ったときのパケット全体の大きさが分かります。

UDPのヘッダーは、送信元ポート・宛先ポート・長さ・チェックサムの4つで、各2バイト、合わせて8バイトです。 TCPは最小20バイトで、タイムスタンプなどのオプションが付くと増えます。上の tcpdump を -v で見ると、IPパケット全体の大きさが出ます。

$ tcpdump -i lo -nn -v 'port 45616 or port 45617'
IP (tos 0x0, ttl 64, id 54931, offset 0, flags [DF], proto TCP (6), length 58)
    127.0.0.1.42598 > 127.0.0.1.45616: Flags [P.], ..., options [nop,nop,TS val 2832199986 ecr 2617674671], length 6
IP (tos 0x0, ttl 64, id 28271, offset 0, flags [DF], proto UDP (17), length 34)
    127.0.0.1.40190 > 127.0.0.1.45617: UDP, length 6
34バイトUDPで6バイト送ったIP 20 + UDP 8 + データ 6
58バイトTCPで6バイト送ったIP 20 + TCP 32 + データ 6
8個TCPのパケット数UDPは1個
UDPヘッダーの欄 大きさ 役割
送信元ポート 2バイト 返事の宛先。使わないなら0でもよい(RFC 768)
宛先ポート 2バイト 届け先のアプリケーション
長さ 2バイト ヘッダーを含むデータグラムの長さ(最小8)
チェックサム 2バイト 壊れていないかの検査。IPv4では0(省略)も許される。IPv6では必須

TCPヘッダーには、このほかにシーケンス番号・確認応答番号・フラグ・ウィンドウサイズが入ります。手元のTCPは「TS val … ecr …」のタイムスタンプオプション(12バイト)が付いて32バイトでした。

PythonでUDPとTCPを送受信する(最小のコード)

この節では、UDPとTCPのサーバーとクライアントを Python で書くと何が違うのかが、動かした結果で分かります。

違いは SOCK_DGRAM か SOCK_STREAM かと、TCPにだけ listen・accept・connect があることです。

# udp_server.py
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)   # UDP
s.bind(("127.0.0.1", 45620))
while True:
    data, addr = s.recvfrom(65535)                   # 1 データグラム = 1 回の recvfrom
    print(f"受信 {len(data)} バイト from {addr}: {data!r}")
    s.sendto(data.upper(), addr)                     # 相手へそのまま返す

# udp_client.py
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.settimeout(2)
s.sendto(b"hello udp", ("127.0.0.1", 45620))        # connect() も accept() も無い
data, addr = s.recvfrom(65535)
print("応答:", data, addr)
# tcp_server.py
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # TCP
s.bind(("127.0.0.1", 45621)); s.listen()
while True:
    conn, addr = s.accept()                          # 接続(3ウェイハンドシェイク)が済んでから返る
    with conn:
        data = conn.recv(65535)                      # バイト列の一部だけが返ることもある
        conn.sendall(data.upper())

# tcp_client.py
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("127.0.0.1", 45621))                     # ここで接続を確立する
s.sendall(b"hello tcp")
print("応答:", s.recv(65535))
$ python3 udp_client.py
応答: b'HELLO UDP' ('127.0.0.1', 45620)
$ python3 tcp_client.py
応答: b'HELLO TCP'

待ち受けが無いときの違い

サーバーを止めて同じクライアントを動かすと、エラーの出方が違いました。

$ python3 udp_client.py          # sendto() は成功し、返事を待って時間切れになる
    data, addr = s.recvfrom(65535)
TimeoutError: timed out

$ python3 tcp_client.py          # connect() の時点で断られる
    s.connect(("127.0.0.1", 45621))
ConnectionRefusedError: [Errno 111] Connection refused

UDPの sendto() は、相手がいなくても成功します。 送った後に何が起きたかを UDP 自身は知らないためです。相手がいないことを知る手がかりは、次の ICMP の節で扱う「port unreachable」だけで、それも connect() したソケットでないと受け取れません。

データグラムの境界が残る・残らない

3つのメッセージ aaa・bbb・ccc を続けて送り、受け取る側が少し待ってから読んだ結果です。

UDP recvfrom -> b'aaa'
UDP recvfrom -> b'bbb'
UDP recvfrom -> b'ccc'
TCP recv     -> b'aaabbbccc'

UDPは1回の送信が1回の受信に対応します。TCPはバイトの流れなので、3回の送信が1回の recv にまとまって返りました。TCPでメッセージの区切りが要るときは、長さを先頭に付けるか改行で区切るなど、アプリケーション側で決めます。

UDPは届かない・順番が変わる:ip netnsで損失30%を起こした結果

この節では、本当にUDPのデータが消えるのか、順番が変わるのかが、損失と遅延を作った経路で動かした結果で分かります。

中継の名前空間で、受け取る側へ向かうパケットを30%の確率で捨てる規則を入れ、1〜20の番号を付けた1,000バイトのメッセージを20ミリ秒おきに20個送りました。UDPは7個消え、TCPは20個すべて届きました。

# 中継(t13r)の中だけに入れた規則。ホストの設定は変えていない
$ ip netns exec t13r nft add table ip t13
$ ip netns exec t13r nft 'add chain ip t13 lossy { type filter hook forward priority 0; }'
$ ip netns exec t13r nft add rule ip t13 lossy ip daddr 10.213.1.2 numgen random mod 100 '<' 30 counter drop
===== 損失 30%
UDP 届いた順: [1, 3, 5, 6, 8, 10, 12, 13, 14, 15, 16, 18, 20]
UDP 届いた数: 13/20  失われた番号: [2, 4, 7, 9, 11, 17, 19]  順番の入れ替わり: 0 箇所
TCP 届いた順: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
TCP 届いた数: 20/20  受信にかかった時間: 0.41 秒

$ ip netns exec t13a ping -c 20 -i 0.2 10.213.1.2
20 packets transmitted, 12 received, 40% packet loss, time 3875ms

送る側のプログラムは、UDPでもTCPでも「20個送信 完了」と表示して終わりました。UDPでは、送る側は7個消えたことを知る手段がありません。 ping(ICMP)も同じ経路なので、40%が返ってきませんでした。

TCPはどう直したか:再送とSACKの原文

同じ損失30%で、送る側の tcpdump を見ると、TCPが足りない部分をどう埋めたかが分かります。

10.213.0.1.47270 > 10.213.1.2.45631: Flags [P.], seq 7:9, ack 1, ..., length 2
10.213.1.2.45631 > 10.213.0.1.47270: Flags [.], ack 9, ...
10.213.0.1.47270 > 10.213.1.2.45631: Flags [P.], seq 9:11, ack 1, ..., length 2       ← 途中で捨てられた
(0.207 秒、ACK が来ない)
10.213.0.1.47270 > 10.213.1.2.45631: Flags [P.], seq 11:37, ack 1, ..., length 26     ← 待っている間に溜まった分
10.213.1.2.45631 > 10.213.0.1.47270: Flags [.], ack 9, ..., options [...,sack 1 {11:37}]  ← 「9 まで届いた。11〜37 も持っている」
10.213.0.1.47270 > 10.213.1.2.45631: Flags [P.], seq 9:11, ack 1, ..., length 2       ← 9:11 だけ再送
10.213.1.2.45631 > 10.213.0.1.47270: Flags [.], ack 37, ...                            ← 37 まで揃った
  • 受け取る側は、抜けた seq 9:11 より後ろのデータを捨てずに持ち、SACK(選択的確認応答)で「11〜37 は持っている」と伝えました。 送る側は抜けた2バイトだけを送り直しています。
  • その間、アプリケーションには何も渡されません。抜けが埋まるまで0.2秒止まり、揃った瞬間にまとめて渡されました。TCPの「確実」は「待つ」ことで成り立っています。

遅延と順番の入れ替わり

中継の tc(HTB)で、IPヘッダーの識別番号が奇数のパケットだけを 100kbit/s の遅いクラスに回し、半分が遅れて届く経路を作りました。

# 中継(t13r)の受け取る側向きのインターフェースだけに付けた
$ ip netns exec t13r tc qdisc add dev v13rb root handle 1: htb default 10
$ ip netns exec t13r tc class add dev v13rb parent 1: classid 1:10 htb rate 100mbit
$ ip netns exec t13r tc class add dev v13rb parent 1: classid 1:20 htb rate 100kbit ceil 100kbit
$ ip netns exec t13r tc filter add dev v13rb parent 1: protocol ip prio 1 u32 match u16 0x0001 0x0001 at 4 flowid 1:20
===== 半分のパケットを遅い経路に回す
UDP 届いた順: [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 10, 14, 15, 17, 16, 20, 18, 19]
UDP 届いた数: 20/20  失われた番号: []  順番の入れ替わり: 3 箇所
TCP 届いた順: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
TCP 届いた数: 20/20  受信にかかった時間: 0.63 秒(通常は 0.41 秒)

UDPは、10 が 13 の後に、16 が 17 の後に届きました。 送った順に処理したいアプリケーションは、自分で番号を付けて並べ直す必要があります。TCPは並べ直して順に渡した代わりに、0.2秒長くかかりました。

1

損失

UDPは消えた番号が空く。TCPは再送で埋めるが、埋まるまで後ろのデータも渡されない。

20個中7個が消えた

2

順番

UDPは届いた順のまま。TCPは並べ直す。

3箇所で入れ替わった

3

時間

UDPは遅れても止まらない。TCPは揃うまで待つ。

0.41秒 → 0.63秒

4

送る側の見え方

UDPもTCPも送信は「完了」と出る。違いは受け取る側にだけ現れる。

送信側のログは同じ

UDPは必ず速いのか:20万個送って半分が消えた実測

この節では、「UDPは速い」がどこまで本当なのかが、127.0.0.1 での実測で分かります。

100バイトのメッセージを20万個、できる限り速く送りました。UDPは受け取る側の処理が追いつかず、損失の無いループバックでも 48.8% が消えました。 TCPは流量を調整しながら全部届け、送信にかかった時間も短かったです。

$ python3 blast.py
UDP: 200000 個を 0.82 秒で送信、届いた数 102318 (51.2%)
TCP: 200000 個を 0.38 秒で送信、届いた数 200000 (100.0%)

$ nstat -az | grep Udp
UdpInErrors                     97682
UdpRcvbufErrors                 97682        ← 受信バッファがあふれて捨てた数(200000 − 102318 と一致)
  • UDPの「速い」は、往復の待ちとヘッダーが小さいという意味です。受け取る側のバッファ(既定 net.core.rmem_default = 212,992 バイト)があふれれば、ネットワークの途中でなくても捨てられます。
  • TCPが速かったのは、カーネルが小さな送信をまとめて大きなセグメントにし、相手のウィンドウに合わせて送ったためです。小さなメッセージを大量に送る用途では、UDPが速いとは限りません。
  • UDPで送る側は、送る速さを自分で抑える必要があります。流量の調整も「しないこと」の1つです。

UDPが使われる理由:DNS・動画・ゲーム・QUIC

この節では、どんな通信がUDPを選び、なぜ選ぶのかが分かります。

共通するのは「1往復で終わる」か「遅れたデータに価値が無い」かのどちらかです。

用途 UDPを選ぶ理由 欠けたときの扱い
DNS(名前解決) 問い合わせ1個・答え1個。接続の往復が無駄 時間切れで問い合わせ直す。答えが512バイトを超えるとTCPに切り替える
DHCP IPアドレスが決まる前に、ブロードキャストで送る必要がある 再送はDHCP自身が行う
NTP(時刻合わせ) 往復の時間そのものを測るので、再送で遅れた応答は役に立たない 次の問い合わせで補う
音声・動画の通話(RTP) 0.1秒前の音声を再送されても再生に間に合わない 欠けた分は飛ばす・補間する
オンラインゲーム 位置の情報は最新の1個だけが要る 次の更新で上書きされる
QUIC(HTTP/3) 再送・順序・暗号を自分で持ち、1つの損失で全部が止まるのを避ける ストリーム単位で再送する

DNSの問い合わせを実際に流す

名前空間の中に dnsmasq を立て、Python で組み立てたDNSの問い合わせをUDPの53番へ送りました。

q = struct.pack("!HHHHHH", 0x1234, 0x0100, 1, 0, 0, 0)          # ID, 再帰要求, 質問 1 件
q += b"".join(bytes([len(p)]) + p.encode() for p in name.split(".")) + b"\0"
q += struct.pack("!HH", 1, 1)                                     # A レコード, IN
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM); s.settimeout(2)
s.sendto(q, (server, 53))                                         # UDP 53 番へ 1 データグラム
r, _ = s.recvfrom(512)
$ ip netns exec t13a python3 dnsq.py www.example.test 10.213.1.2
質問 34 バイト → 応答 50 バイト / 応答の末尾 4 バイト(IPv4): 10.213.1.77

$ tcpdump -i v13a -nn 'port 53'
10.213.0.1.43039 > 10.213.1.2.53: 4660+ A? www.example.test. (34)
10.213.1.2.53 > 10.213.0.1.43039: 4660* 1/0/0 A 10.213.1.77 (50)

パケットは2個だけです。 TCPなら接続と切断で少なくとも7個増えます。DNSが「普段はUDP、大きな応答やゾーン転送はTCP」なのはこのためで、ファイアウォールで UDP の53番だけ開けていると、TCPに切り替わる応答が通らなくなります。

QUICとHTTP/3:UDPの上にTCPの機能を作り直した

QUIC(RFC 9000)は、UDPの上で、接続・再送・順序・流量の調整・TLS 1.3 の暗号化をまとめて行うプロトコルで、HTTP/3(RFC 9114)がその上で動きます。TCPではなくUDPを土台にした理由は、カーネルを変えずにユーザー空間で改良できることと、TCPで1つのパケットが欠けると後続の全データが待たされる(上の再送の原文のとおり)問題を、ストリームごとの再送で避けるためです。

HTTPの版 トランスポート 接続の確立
HTTP/1.1 TCP(+TLS) TCP 1往復 + TLS 1〜2往復
HTTP/2 TCP(+TLS) 同上。1接続で多重化する
HTTP/3 QUIC(UDP 443番) QUIC が暗号化と接続を1往復で済ませる

手元の curl 8.5.0(Ubuntu)は HTTP/3 を含まない構成だったため、HTTP/3 の通信は公式の仕様で確かめています。

UDPとTCPのポート一覧とファイアウォール

この節では、UDPとTCPのどちらで動くサービスが何番を使うかと、ポートの指定で間違えやすい点が分かります。

ポート番号はUDPとTCPで別の空間です。 「53番」と言うだけでは足りず、UDPの53番とTCPの53番は別の口です。

ポート サービス 使うプロトコル
53 DNS UDPとTCPの両方
67・68 DHCP(サーバー・クライアント) UDP
123 NTP UDP
161・162 SNMP・SNMPトラップ UDP
443 HTTPS(HTTP/1.1・2)/HTTP/3 TCP/UDP(QUIC)
514 syslog UDP(TCP版もある)
22・25・80・110・143 SSH・SMTP・HTTP・POP3・IMAP TCP
33434〜 traceroute の探り(Linux の既定) UDP

どのポートを誰が使っているかは ss で見ます。UDPの待ち受けには状態がありません。

$ ss -tuln
Netid State  Recv-Q Send-Q Local Address:Port  Peer Address:Port
udp   UNCONN 0      0          127.0.0.1:45620      0.0.0.0:*
tcp   LISTEN 0      128        127.0.0.1:45621      0.0.0.0:*

$ ss -tunp          # TCP は接続ごとに ESTAB の行が増える。UDP には接続の行が無い
tcp   ESTAB 0      0          127.0.0.1:51350     127.0.0.1:45621 users:(("python3",pid=17882,fd=3))
tcp   ESTAB 0      0          127.0.0.1:45621     127.0.0.1:51350 users:(("python3",pid=17877,fd=4))
  • 1ファイアウォールの規則は UDP と TCP を別に書くDNS は UDP 53 と TCP 53 の両方。片方だけだと大きな応答が通らない
  • 2UDP の疎通は「応答が返るか」でしか分からないnc -zv は TCP では refused と出るが、UDP では何も出ずに終わった(下の表)
  • 3ICMP を全部止めないport unreachable や分割が必要の通知が届かず、原因が見えなくなる
  • 4ss -tuln で待ち受けを確かめるu が UDP、t が TCP。UDP は UNCONN と表示される

ポート番号の全体の一覧は、関連記事「ポート番号一覧【2026年版】well-knownポートと確認コマンドの早見表」で扱っています。

ICMPとは:ping・traceroute・到達不能とUDPの関係

この節では、ICMPが何を運ぶプロトコルで、UDPの通信とどう関わるのかが、名前空間の中で流した原文で分かります。

ICMP(Internet Control Message Protocol)は、IPの通信で起きたエラーの通知と、疎通の診断を運ぶプロトコルです(RFC 792)。 UDPやTCPと同じくIPの上で動きますが、アプリケーションのデータを運ぶものではなく、ポート番号もありません。

タイプ/コード 意味 手元で見た場面
8 → 0 Echo(要求)→ Echo Reply(応答) ping
3/0 Destination Unreachable:net unreachable 経路の無い宛先に ping
3/1 host unreachable 同じLANにいない相手に ping
3/3 port unreachable 閉じたUDPポートへ送った・traceroute の最後
3/4 fragmentation needed and DF set MTUより大きいパケットをDF付きで送ったとき(経路MTU探索)
11/0 Time Exceeded:TTLが0になった traceroute の途中

pingとtracerouteの原文

送る側の名前空間(10.213.0.1)から、中継(10.213.0.254)を挟んだ 10.213.1.2 へ traceroute と ping を行い、同時に tcpdump で見ました。

$ ip netns exec t13a traceroute -n -q 1 10.213.1.2
traceroute to 10.213.1.2 (10.213.1.2), 30 hops max, 60 byte packets
 1  10.213.0.254  0.043 ms
 2  10.213.1.2  0.010 ms

$ ip netns exec t13a ping -c 2 10.213.1.2
64 bytes from 10.213.1.2: icmp_seq=1 ttl=63 time=0.025 ms
64 bytes from 10.213.1.2: icmp_seq=2 ttl=63 time=0.043 ms

$ tcpdump -i v13a -nn 'icmp or udp'
10.213.0.1.48036 > 10.213.1.2.33434: UDP, length 32                        ← traceroute の探り(TTL=1)
10.213.0.254 > 10.213.0.1: ICMP time exceeded in-transit, length 68        ← 中継が TTL 切れを通知
10.213.0.1.51787 > 10.213.1.2.33435: UDP, length 32                        ← 探り(TTL=2)
10.213.1.2 > 10.213.0.1: ICMP 10.213.1.2 udp port 33435 unreachable, length 68  ← 終点に着いた
10.213.0.1 > 10.213.1.2: ICMP echo request, id 17901, seq 1, length 64     ← ping
10.213.1.2 > 10.213.0.1: ICMP echo reply, id 17901, seq 1, length 64
1

traceroute は UDP を送っている

Linux の traceroute は既定で、33434番から1つずつ増やしたUDPポートへ、TTLを1・2・3…と増やしながら送る。TCPでもICMPでもない。

UDP 33434〜

2

途中の機器は ICMP Time Exceeded を返す

TTLが0になった機器が、自分のアドレスを送信元にして返す。これが1行目の 10.213.0.254。

type 11

3

終点は ICMP Port Unreachable を返す

誰も待ち受けていないUDPポートに届いたので、終点が「そのポートは閉じている」と返す。これで終点に着いたと分かる。

type 3 code 3

4

ping は Echo と Echo Reply

ttl=63 は、中継を1つ通って64から1減ったことを示す。

type 8 → 0

到達不能の3種類

$ ip netns exec t13a ping -c 3 10.213.0.99          # 同じ LAN にいない相手(ARP の応答が無い)
From 10.213.0.1 icmp_seq=2 Destination Host Unreachable
From 10.213.0.1 icmp_seq=3 Destination Host Unreachable
3 packets transmitted, 0 received, +3 errors, 100% packet loss, time 2038ms

$ ip netns exec t13a ping -c 2 10.213.9.9           # 中継に経路が無い
From 10.213.0.254 icmp_seq=1 Destination Net Unreachable
From 10.213.0.254 icmp_seq=2 Destination Net Unreachable

$ python3 -c "...sendto(b'hi', ('10.213.1.2', 9999))"   # 閉じた UDP ポート
10.213.0.1.43496 > 10.213.1.2.9999: UDP, length 2
10.213.1.2 > 10.213.0.1: ICMP 10.213.1.2 udp port 9999 unreachable, length 38
  • 「From 誰」が原因の場所を教えます。 Host Unreachable は自分(10.213.0.1)が出していて、隣の相手が見つからないという意味です。Net Unreachable は中継(10.213.0.254)が出していて、そこから先の経路が無いという意味です。
  • UDPで閉じたポートへ送ると、ICMP の port unreachable が返ります。TCPなら RST が返り、Connection refused になります。UDPでこのエラーをプログラムが受け取るには、connect() したソケットで送る必要があります。
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("127.0.0.1", 45620))   # UDP でも connect() はできる(宛先を固定するだけ)
s.send(b"first")                  # 成功する(まだ何も分からない)
time.sleep(0.1)
s.send(b"second")                 # ICMP port unreachable を受け取った後なので失敗する
    s.send(b"second")
ConnectionRefusedError: [Errno 111] Connection refused

1回目の送信は成功し、2回目で初めて Connection refused になりました。UDPの「Connection refused」は、前の送信に対するICMPを受け取った結果です。

ICMPが返らないこともある

traceroute の原文をよく見ると、終点からの port unreachable は 33435〜33440 の6個だけで、残りの探りには応答がありませんでした。Linux はカーネルのドキュメントのとおり、Destination Unreachable・Time Exceeded などの ICMP の送信回数を既定で制限しています(icmp_ratelimit = 1000 ミリ秒、icmp_ratemask で対象を指定)。ファイアウォールが ICMP を止めていることもあります。ICMP が返らない=相手がいない、ではありません。 ping が通らないときの切り分けは、関連記事「ネットワークの切り分け8選|pingは通るのに繋がらない」で、traceroute の読み方は、関連記事「traceroute・tracertの使い方|見方と* * *・!Hの意味を実例で」で扱っています。

UDPの落とし穴:最大サイズ・分割・境界・connect()

この節では、UDPでプログラムを書くときに知らないとつまずく点が分かります。

  1. 11データグラムの最大は 65,507 バイトIPの長さの欄が16ビットなので、65,535 − IP 20 − UDP 8。超えると Message too long
  2. 2MTU を超えるとIPの層で分割される3,000 バイトは1,500のMTUで3つに分かれた。1つ欠けると全部が捨てられる
  3. 3recvfrom のバッファが小さいと残りは捨てられるTCPのように次の recv で続きは読めない
  4. 4sendto の成功は「届いた」ではない相手がいなくても成功する。応答か時間切れで判断する
  5. 5connect() すると ICMP のエラーが見える宛先を固定するだけで、ハンドシェイクは起きない
  6. 6送信元アドレスは偽装できる接続の手続きが無いため。公開するUDPサービスは応答の大きさと回数を抑える
$ ip netns exec t13a python3 -c "...sendto(b'x'*3000, ...); sendto(b'x'*70000, ...)"
3000 バイト送信
70000 バイト: [Errno 90] Message too long
最大: 65507 バイト

$ tcpdump -i v13b -nn udp            # 3,000 バイトが 3 つの IP パケットに分かれた
10.213.0.1.35245 > 10.213.1.2.9999: UDP, length 3000
10.213.0.1 > 10.213.1.2: ip-proto-17
10.213.0.1 > 10.213.1.2: ip-proto-17

2つ目以降の断片には UDP ヘッダーが無いため、tcpdump は ip-proto-17 としか表示できません。分割を避けたいアプリケーション(DNS・QUIC)は、1データグラムを 1,200〜1,400 バイト程度に抑えています。パケットの中身を目で追う方法は、関連記事「Wiresharkの使い方|画面の見方・フィルター早見表を実例で解説」で扱っています。

よくあるエラー・つまずき(原文つき)

この節では、UDPとTCPのまわりで出るエラーや「おかしい」の原文と、その意味が分かります。

原文・症状 意味 対処
TimeoutError: timed out(UDP の recvfrom) 相手がいない・届いていない・応答が捨てられた 宛先とポート、ファイアウォール(UDP の規則)、サーバーの待ち受け(ss -uln)を確かめる
ConnectionRefusedError: [Errno 111](TCP の connect) 相手がそのポートで待ち受けていない(RST が返った) サーバーの起動とポート番号、127.0.0.1 と 0.0.0.0 の違いを確かめる
ConnectionRefusedError(UDP の2回目の send) 前の送信に ICMP port unreachable が返った 相手のUDPの待ち受けを確かめる。connect() しないソケットではこのエラーは出ない
[Errno 90] Message too long 1データグラムが 65,507 バイトを超えた 分割して送る。実用上は MTU 以下(1,200〜1,400)に抑える
UdpRcvbufErrors が増える 受け取る側のバッファがあふれて捨てている 受け取る側の処理を速くする・バッファを広げる・送る側が速度を抑える
Destination Host Unreachable 同じLANの相手が見つからない(ARP 応答なし) アドレス・ケーブル・相手の電源・VLAN を確かめる
Destination Net Unreachable(From が中継) 中継にその先の経路が無い From に出た機器の経路表を確かめる
nc -zv -u が何も言わずに終わる UDP は接続しないので「開いている」とは言えない 実際にそのサービスの問い合わせを送って応答を見る
DNS で小さな名前は引けるが大きな応答が返らない TCP 53 が通っていない ファイアウォールで UDP 53 と TCP 53 の両方を許可する

よくある誤解

誤解 手元で確かめた事実
UDPは必ずTCPより速い 20万個の小さなメッセージでは、UDPは半分が消え、送信時間もTCPのほうが短かった
TCPなら絶対に届く 経路が切れれば届かない。TCPが保証するのは「届いたか分かる」「届いたものは順番どおり」
UDPは一方通行 DNSの問い合わせと応答のように双方向で使える。送信元ポートに返事が来る
同じポート番号ならUDPとTCPは同じ通信 別の空間。UDP 53 と TCP 53 は別の待ち受け
UDPは送ったらすぐ消える(信頼性ゼロ) 損失の無い経路では20個すべて順番どおり届いた。「保証しない」であって「届かない」ではない
sendto が成功したから届いた 相手がいなくても成功する
pingが通ればUDPも通る ICMPとUDPはファイアウォールで別に扱われる。逆に ICMP が止められていても UDP は通ることがある

UDPとTCPの使い分け判断表

この節では、自分の通信にUDPとTCPのどちらを使うかを決める順番が分かります。

問い はい いいえ
1バイトでも欠けると意味が無いか(ファイル・決済・API) TCP 次へ
遅れて届いたデータは捨ててよいか(音声・映像・位置) UDP 次へ
1往復で終わり、相手の数が多いか(DNS・NTP・ログ) UDP(再送は自分で) 次へ
ブロードキャスト・マルチキャストで送るか UDP(TCPではできない) 次へ
再送・順序・暗号を自分で作る余力があるか UDP(または QUIC のライブラリ) TCP(+TLS)
迷っている TCP。UDPを選ぶ理由を説明できるときだけUDPにする
  • Webアプリケーションや業務システムの大半は、HTTP(TCP)を使うので、UDPを自分で扱う場面は限られます。 扱うのは、動画・音声・IoTの計測値・ゲーム・ネットワーク機器のログ(syslog・SNMP)・DNSやDHCPの運用のときです。
  • HTTP/3 を有効にすると、アプリケーションのコードを変えずにUDP(QUIC)の恩恵を受けられます。その代わり、ファイアウォールと負荷分散装置でUDPの443番を通す設定が要ります。

よくある質問

Q. UDPとは何ですか? A. User Datagram Protocol の略で、IPの上でアプリケーション同士がデータグラム(ひとかたまりのデータ)をやり取りするトランスポート層のプロトコルです。接続・再送・順序の保証をせず、8バイトのヘッダーで送ります。仕様は RFC 768 です。

Q. UDPとTCPの違いを一言で言うと? A. TCPは「つないで、確かめて、足りなければ送り直す」、UDPは「宛先を付けて投げる」です。手元では同じ1行を送るのに、TCPは8パケット、UDPは1パケットでした。

Q. UDPは本当にデータが消えるのですか? A. 消えることがあります。30%の損失を起こした経路では、20個のうち7個が届きませんでした。損失の無い経路では20個すべて届いたので、「必ず消える」のではなく「消えても誰も直さない」が正確です。

Q. UDPはTCPより速いですか? A. 接続の往復が無くヘッダーも小さいので、1往復で終わる通信や遅延を嫌う通信では有利です。ただし、小さなメッセージを大量に送った実測では、受け取る側のバッファがあふれて半分が消え、送信時間もTCPのほうが短かったです。

Q. ICMPとは何ですか?UDPとどう関係しますか? A. IPのエラー通知と診断を運ぶプロトコルで、ping(Echo)と traceroute(Time Exceeded)が使います。UDPで閉じたポートに送ると、ICMPの port unreachable が返ります。traceroute は UDP を送って ICMP を受け取る仕組みです。

Q. ICMPはTCP・UDPと同じ層ですか? A. IPの上で動く点は同じですが、ポート番号を持たず、アプリケーションのデータを運ばない点が違います。プロトコル番号は ICMP が1、TCP が6、UDP が17です。

Q. DNSはUDPとTCPのどちらですか? A. 普段はUDPの53番で、応答が大きいとき(512バイトを超える・TCビットが立つ)やゾーン転送はTCPの53番を使います。ファイアウォールでは両方を許可します。

Q. なぜHTTP/3はUDPを使うのですか? A. QUIC がUDPの上で接続・再送・暗号化を自分で行うためです。TCPでは1つのパケットの損失で後ろのデータ全部が待たされますが、QUIC はストリームごとに再送するので、ほかのストリームは止まりません。

Q. UDPのポートが開いているか調べるには? A. 接続が無いので「開いている」を外から確かめる決まった方法はありません。閉じていれば ICMP port unreachable が返ることがありますが、ICMPは制限や遮断で返らないこともあります。確実なのは、そのサービスの問い合わせを送って応答を見ることです。

Q. UDPで確実に届けたいときは? A. アプリケーションで番号を付け、応答(ACK)と再送を自分で作ります。それが必要なら、最初からTCPか、QUICのライブラリを使うほうが確実です。

Q. ビデオ通話で音が途切れるのはUDPのせいですか? A. 途切れの原因は経路の損失や遅延で、UDPは「再送して遅らせる」代わりに「欠けた部分を飛ばす」ことを選んでいます。TCPにすると途切れの代わりに全体が遅れます。

Q. UDPは危険ですか? A. 接続の手続きが無いので送信元を偽装しやすく、公開するUDPサービスは応答の大きさと回数を抑える設計が要ります。社内の通信や、認証と暗号化を持つQUICのような上位プロトコルで使う分には、TCPと同じように使えます。

まとめ

  • UDPは、接続・再送・順序の保証をしない代わりに、8バイトのヘッダーで1パケットで送れる。TCPは同じ1行に8パケット使った。
  • 損失30%の経路で、UDPは20個中7個が消え、順番も入れ替わった。TCPは再送とSACKで全部を順番どおり届けたが、その間0.2秒止まった。
  • 「UDPは必ず速い」ではない。受け取る側が追いつかないと、手元のループバックでも半分が消えた。
  • DNS・DHCP・NTP・音声・動画・ゲーム・QUIC(HTTP/3)がUDPを使う。理由は「1往復で終わる」か「遅れたデータに価値が無い」か。
  • ICMPはIPのエラー通知と診断。ping は Echo、traceroute は UDP を送って Time Exceeded と Port Unreachable を受け取る。ICMPが返らない=相手がいない、ではない。

参考・出典

確認日はいずれも 2026年10月29日です。

  • IETF「RFC 768 User Datagram Protocol」:https://www.rfc-editor.org/rfc/rfc768
  • IETF「RFC 9293 Transmission Control Protocol (TCP)」:https://www.rfc-editor.org/rfc/rfc9293
  • IETF「RFC 792 Internet Control Message Protocol」:https://www.rfc-editor.org/rfc/rfc792
  • IETF「RFC 9000 QUIC: A UDP-Based Multiplexed and Secure Transport」:https://www.rfc-editor.org/rfc/rfc9000
  • IETF「RFC 9114 HTTP/3」:https://www.rfc-editor.org/rfc/rfc9114
  • IETF「RFC 1035 Domain Names - Implementation and Specification」4.2 Transport:https://www.rfc-editor.org/rfc/rfc1035#section-4.2
  • IETF「RFC 7766 DNS Transport over TCP - Implementation Requirements」:https://www.rfc-editor.org/rfc/rfc7766
  • IANA「Protocol Numbers」:https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
  • Linux カーネル「IP Sysctl」icmp_ratelimit・icmp_ratemask:https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt
  • Python「socket — Low-level networking interface」:https://docs.python.org/3/library/socket.html
  • nftables wiki「Numgen」:https://wiki.nftables.org/wiki-nftables/index.php/Numgen

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「UDPとは|TCPとの違い・ICMPとの関係を「届かない」実物で解説」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ
参加受付中

AI駆動開発、 無料セミナー