TCPとUDPの違いを実測で解説|パケット数・ヘッダ・確認コマンド

学習ガイド

TCPとUDPはどちらもTCP/IPのトランスポート層を担うプロトコルで、違いをひと言でいえば「TCPは届いたことを確認しながら送る、UDPは確認せず送りっぱなしにする」です。この違いは概念の話ではなく、パケットを覗けばそのまま目に見えます。この記事では図解だけで終わらせず、Docker上に隔離ネットワークを組んでtcpdumpssncの実際の出力を並べ、同じデータを送ったときにTCPとUDPで何がどう変わるのかをバイト単位で確かめていきます。

先に、この記事で実測した結果をまとめておきます。いずれも後半で出力そのものを掲載します。

実測した項目TCPUDP
5バイト送るのに流れたパケット数8個1個
同じくフレーム総バイト数549バイト47バイト
ヘッダのサイズ(実測)32バイト8バイト
並べ替えが起きる回線での受信順送信順どおりバラバラ
ss -tuln での待ち受け状態LISTENUNCONN
閉じたポートへ接続したとき即座に拒否される成功に見えることがある

TCPとUDPの違いを一覧で比較

細かい仕組みに入る前に、両者の性質を整理します。

比較項目TCPUDP
正式名称Transmission Control ProtocolUser Datagram Protocol
接続方式コネクション型(事前に接続を確立)コネクションレス型(いきなり送る)
到達保証あり(確認応答と再送)なし(ベストエフォート)
順序保証ありなし
フロー制御・輻輳制御ありなし
ヘッダサイズ20バイト以上(オプションで可変)8バイト固定
速度・遅延確認応答の分だけ遅くなる低遅延
1対多の通信不可(1対1のみ)ブロードキャスト・マルチキャスト可
主な用途Web・メール・SSH・ファイル転送DNS・動画/音声・ゲーム・SNMP
IPヘッダ上の番号617

よく「TCPは信頼性が高く、UDPは速い」とまとめられますが、UDPが速いのは「機能が少ないから」であって、回線が速くなるわけではありません。UDPは到達確認も順序整理もアプリケーション側の責任になります。この「責任の置き場所が違うだけ」という感覚が、次の実測で掴めるはずです。

検証環境

以降の出力はすべて、次の環境で実際に取得したものです。自宅や職場のLANでキャプチャすると実際のIPアドレスやMACアドレスが記録に残るため、Dockerで外部から隔離したネットワークを作って検証しています。手元で再現する場合も同じ方法をおすすめします。

# 検証用イメージ(alpine 3.20 に検証ツールを追加)
FROM alpine:3.20
RUN apk add --no-cache tcpdump iproute2 netcat-openbsd python3 iptables
CMD ["sleep", "infinity"]
docker build -t tcpudp-lab .
docker network create --subnet 172.31.0.0/24 tcpudp-net
docker run -d --name srv --network tcpudp-net --ip 172.31.0.10 \
  --cap-add NET_ADMIN --cap-add NET_RAW tcpudp-lab
docker run -d --name cli --network tcpudp-net --ip 172.31.0.20 \
  --cap-add NET_ADMIN --cap-add NET_RAW tcpudp-lab

172.31.0.10をサーバー役(srv)、172.31.0.20をクライアント役(cli)とします。tcpdumpでパケットを覗いたりtcで回線品質を劣化させたりするため、--cap-add NET_ADMIN--cap-add NET_RAWが必要です。

【実測1】同じ5バイトを送るのに、TCPは8パケット・UDPは1パケット

まず、まったく同じ「HELLO」という5バイトを、TCPとUDPそれぞれで1回送ります。サーバー側でtcpdumpを動かしながら受信します。

TCPで送った場合

# サーバー側:キャプチャと待ち受けを開始
tcpdump -i eth0 -n "tcp port 9000" -w /tmp/tcp.pcap &
nc -l -p 9000 > /tmp/tcp_recv.txt &

# クライアント側:5バイト送る
echo -n "HELLO" | nc -q 1 172.31.0.10 9000

キャプチャを読み出した結果が次の8行です。

$ tcpdump -r /tmp/tcp.pcap -n
172.31.0.20.53646 > 172.31.0.10.9000: Flags [S],  seq 701397311,                 length 0
172.31.0.10.9000  > 172.31.0.20.53646: Flags [S.], seq 1776532157, ack 701397312, length 0
172.31.0.20.53646 > 172.31.0.10.9000: Flags [.],                  ack 1,          length 0
172.31.0.20.53646 > 172.31.0.10.9000: Flags [P.], seq 1:6,        ack 1,          length 5
172.31.0.10.9000  > 172.31.0.20.53646: Flags [.],                  ack 6,          length 0
172.31.0.20.53646 > 172.31.0.10.9000: Flags [F.], seq 6,          ack 1,          length 0
172.31.0.10.9000  > 172.31.0.20.53646: Flags [F.], seq 1,         ack 7,          length 0
172.31.0.20.53646 > 172.31.0.10.9000: Flags [.],                  ack 2,          length 0

実際にデータが入っているのは4行目の1個(length 5)だけで、残りの7個は「段取り」と「後片付け」です。フラグの意味を対応させると、TCPの動きがそのまま読み取れます。

フラグ意味
1〜3[S][S.][.]3ウェイハンドシェイク(SYN → SYN+ACK → ACK)で接続を確立
4[P.]データ本体の送信(PSH+ACK、length 5
5[.]サーバーからの確認応答ack 6=5バイト受け取った)
6〜8[F.][F.][.]FINによる接続の切断処理

5行目のack 6に注目してください。「6バイト目から先を待っている」=「5バイト目までは確実に受け取った」という宣言で、これがTCPの到達保証の実体です。この応答が返ってこなければ、送信側は同じデータを再送します。

UDPで送った場合

# サーバー側
tcpdump -i eth0 -n "udp port 9000" -w /tmp/udp.pcap &
nc -u -l -p 9000 > /tmp/udp_recv.txt &

# クライアント側
echo -n "HELLO" | nc -u -w 1 172.31.0.10 9000
$ tcpdump -r /tmp/udp.pcap -n
172.31.0.20.55683 > 172.31.0.10.9000: UDP, length 5

1行だけです。接続の確立もなければ、受け取ったという返事もありません。それでもサーバー側のファイルにはHELLOが届いています。

流れた総バイト数を数える

キャプチャからフレームの総バイト数を合計すると、差はさらにはっきりします。

TCP: 549 bytes / 8 packets
UDP:  47 bytes / 1 packets

同じ5バイトを届けるのに、TCPはUDPの約11.7倍の通信量を使っています。「UDPは軽い」というのは、この差のことです。

ただし誤解しないでください。この比率は「5バイトだけ送る」という極端な条件だからこそ大きく開きます。ハンドシェイクと切断は接続ごとに1回しか発生しないため、1MBのファイルを転送するようなケースでは、TCPのオーバーヘッドは全体の1%にも届きません。UDPが有利なのは、DNS問い合わせのように「小さなデータを1往復で終わらせたい」場面です。

【実測2】ヘッダを16進ダンプでバイト単位に数える

「TCPヘッダは20バイト以上、UDPヘッダは8バイト」という説明は必ず出てきますが、実際に数えてみると理解が変わります。tcpdump -XXでフレーム全体を16進表示します。

UDPパケットの中身(全47バイト)

$ tcpdump -r /tmp/udp.pcap -n -e -XX
length 47: 172.31.0.20.55683 > 172.31.0.10.9000: UDP, length 5
        0x0000:  fea2 8b32 9e98 060e 3906 355f 0800 4500  ...2....9.5_..E.
        0x0010:  0021 d790 4000 4011 0adf ac1f 0014 ac1f  .!..@.@.........
        0x0020:  000a d983 2328 000d 587b 4845 4c4c 4f    ....#(..X{HELLO

先頭から順に区切ると、47バイトの内訳はこうなります。

位置中身サイズ読み方
0x000x0dEthernetヘッダ14宛先MAC・送信元MAC・0800(IPv4)
0x0e0x21IPヘッダ2045=IPv4/ヘッダ20バイト、11プロトコル番号17(UDP)
0x220x29UDPヘッダ8d983 2328 000d 587b
0x2a0x2eデータ本体54845 4c4c 4fHELLO

UDPヘッダの8バイトは、これだけしかありません。

16進10進フィールド
d98355683送信元ポート番号
23289000宛先ポート番号
000d13長さ(ヘッダ8+データ5)
587bチェックサム

「相手のポート番号」と「長さ」と「チェックサム」だけ。順序番号も確認応答番号もありません。UDPが順序を保証できないのは意地悪をしているのではなく、そもそも順序を表す欄がヘッダに存在しないからです。

IPアドレスも読み取れます。ac1f 0014ac=172、1f=31、00=0、14=20なので172.31.0.20、続くac1f 000a172.31.0.10です。指定したコンテナのIPと一致します。

TCPパケットの中身(全71バイト)

$ tcpdump -r /tmp/tcp.pcap -n -e -XX "tcp[tcpflags] & tcp-push != 0"
length 71: 172.31.0.20.53646 > 172.31.0.10.9000: Flags [P.], length 5
        0x0000:  fea2 8b32 9e98 060e 3906 355f 0800 4500  ...2....9.5_..E.
        0x0010:  0039 d0e2 4000 4006 1180 ac1f 0014 ac1f  .9..@.@.........
        0x0020:  000a d18e 2328 29ce 7940 69e3 babe 8018  ....#().y@i.....
        0x0030:  01f6 5888 0000 0101 080a f7ca 5545 f9b0  ..X.........UE..
        0x0040:  b133 4845 4c4c 4f                        .3HELLO

データは同じHELLOの5バイトなのに、フレーム全体は47バイトから71バイトへ、24バイト増えています。増えた分はすべてTCPヘッダです。

  • 0x0eのIPヘッダにある06プロトコル番号6(TCP)。UDPの11(17)と入れ替わっている
  • 0x22からがTCPヘッダで、d18e=53646(送信元ポート)、2328=9000(宛先ポート)
  • 続く29ce 7940シーケンス番号69e3 babe確認応答番号。UDPには存在しなかった欄
  • 8018の上位1桁8がデータオフセットで、8×4=32バイトがTCPヘッダ長。残りの018がフラグで、0x10(ACK)+0x08(PSH)=0x18
  • 01f6=502がウィンドウサイズ。フロー制御に使われる欄で、これもUDPにはない

TCPヘッダは固定20バイトですが、今回は末尾に12バイトのオプション(0101 080a ...=NOP・NOP・タイムスタンプ)が付いて32バイトになっています。「20バイト以上」と説明される理由がここで確認できます。

IPヘッダの全長フィールドで検算もできます。UDPは0021=33=20(IP)+8(UDP)+5(データ)、TCPは0039=57=20(IP)+32(TCP)+5(データ)。それぞれEthernetヘッダ14バイトを足すと、47バイトと71バイトにぴたりと一致します。

【実測3】順序は本当に保証されるのか

「TCPは順序を保証する」を確かめます。通常のLANはほぼ順序どおり届いてしまうので、tcのnetemでクライアントの送出に意図的な並べ替えを発生させます。

# クライアント側:50msの遅延と50%の並べ替えを注入
tc qdisc add dev eth0 root netem delay 50ms reorder 50% 50%

この状態で、001から010まで番号を振ったデータを順番に10個送り、サーバー側が受け取った順序を表示します。

送信順:      001 002 003 004 005 006 007 008 009 010

UDP受信順:   001 002 003 004 005 007 010 006 008 009
TCP受信順:   001 002 003 004 005 006 007 008 009 010

UDPは6番目以降が入れ替わり、TCPは完全に送信順のままです。同じ回線・同じ並べ替え設定で結果が分かれました。

ここで重要なのは、TCPでもパケット自体は並べ替えられているという点です。TCPは受信側でシーケンス番号を見てバッファに並べ直し、順序が揃った分だけをアプリケーションに渡します。実測2で見た29ce 7940というシーケンス番号が、まさにこの並べ直しに使われています。UDPヘッダにはこの番号がないため、届いた順にそのままアプリケーションへ渡すしかありません。

したがって「UDPを使うと順序が狂う」のではなく、「順序を直す仕事がアプリケーション側に回ってくる」が正しい理解です。実際、UDPを使うオンラインゲームや音声通話は、アプリケーション独自に連番を振って対処しています。

検証が終わったら、注入した劣化設定は必ず戻してください。

tc qdisc del dev eth0 root

TCPの仕組み|なぜ確実に届くのか

実測で見た動きを、機能として整理しておきます。

  • 3ウェイハンドシェイク:SYN → SYN+ACK → ACKの3往復で、双方が「送れる・受け取れる」状態にあることを確認してから通信を始める
  • 確認応答(ACK)と再送制御:受信側は「次に何バイト目を待っているか」を返す。一定時間ACKが返らなければ送信側が再送する
  • 順序制御:シーケンス番号で並べ直してからアプリケーションに渡す
  • フロー制御:ウィンドウサイズで「あとどれだけ受け取れるか」を伝え、受信側の処理能力を超えないよう調整する
  • 輻輳制御:ネットワークが混雑してパケットロスが増えると、送信ペースを自動的に落とす

この5つが揃っているため、TCPを使うアプリケーションは「送ったデータは順番どおり全部届く」という前提でコードを書けます。その代わり、接続の確立に最低1往復(RTT)の時間がかかり、パケットロスが起きると再送待ちで一時的に止まります。動画や音声で「少し欠けてもいいから止まらないでほしい」場合にTCPが不利になるのは、この止まる性質のためです。

UDPの仕組み|なぜ速いのか

UDPがやることは、実測2で見たとおり「ポート番号を付けて、長さとチェックサムを添えて送り出す」だけです。

  • 接続の確立がないので、送りたいときに即座に送れる(ハンドシェイクの往復がゼロ)
  • 確認応答を待たないので、パケットが失われても止まらない
  • ヘッダが8バイト固定なので、小さいデータほど効率が良い
  • 宛先を1つに固定しないため、ブロードキャストやマルチキャストで一斉送信できる(TCPは1対1のみ)

DNSがUDPを使う理由もここにあります。名前解決は「短い質問を投げて短い答えをもらう」だけなので、3ウェイハンドシェイクに1往復かけていては本末転倒です。実測1で見たとおり、TCPなら8パケット必要な処理がUDPなら往復2パケットで済みます。

どちらが使われているかをサーバーで確認する

「このサービスはTCPかUDPか」は暗記しなくても、Linuxサーバー上で調べられます。

/etc/services で割り当てを引く

$ grep -E '^(ssh|domain|http|https|ntp|snmp|tftp|syslog)[[:space:]]' /etc/services
ssh             22/tcp          # SSH Remote Login Protocol
domain          53/tcp          # Domain Name Server
domain          53/udp
tftp            69/udp
http            80/tcp          www
ntp             123/udp         # Network Time Protocol
snmp            161/tcp         # Simple Net Mgmt Protocol
snmp            161/udp
https           443/tcp         # http protocol over TLS/SSL
https           443/udp         # HTTP/3
syslog          514/udp

この出力から、暗記より役に立つ事実が3つ読み取れます。

  • domain(DNS)は53番がTCPとUDPの両方に登録されている。通常はUDPを使い、応答が大きい場合やゾーン転送ではTCPに切り替わる
  • snmpも161番がTCP/UDP両方。実運用ではUDPが使われる
  • httpsに443/udpがある。これがHTTP/3(QUIC)で、UDPの上に信頼性の仕組みを自前で載せた新しい方式

最後の項目は、「TCPかUDPか」が固定的な二択ではなくなってきている実例です。HTTP/3はTCPをやめてUDPを土台に選び、再送や順序制御をQUICというプロトコルで実装しています。「UDPは信頼性がない」のではなく「信頼性の仕組みを誰が持つかを選べる」ということです。

dig でDNSがどちらを使ったか確かめる

digは使用したプロトコルを結果に表示してくれます。

$ dig @8.8.8.8 google.com | grep -E "SERVER:|MSG SIZE"
;; SERVER: 8.8.8.8#53(8.8.8.8) (UDP)
;; MSG SIZE  rcvd: 135

$ dig +tcp @8.8.8.8 google.com | grep -E "SERVER:|MSG SIZE"
;; SERVER: 8.8.8.8#53(8.8.8.8) (TCP)
;; MSG SIZE  rcvd: 135

デフォルトでは(UDP)+tcpを付けると(TCP)と表示され、同じ53番ポートでどちらも通ることが確認できます。ファイアウォールでDNSのTCP 53番を閉じてしまい、応答が大きいときだけ名前解決に失敗するというトラブルは、この仕組みを知らないと原因にたどり着けません。

ss で待ち受け中のポートを一覧する

サーバー上で実際に開いているポートはss -tulnで確認します。-tがTCP、-uがUDP、-lが待ち受け中、-nが名前解決の抑制です。

$ ss -tuln
Netid State  Recv-Q Send-Q Local Address:Port  Peer Address:Port
udp   UNCONN 0      0            0.0.0.0:9000       0.0.0.0:*
tcp   LISTEN 0      1            0.0.0.0:9000       0.0.0.0:*

同じ9000番でTCPとUDPの両方を待ち受けた状態です。State欄がTCPはLISTEN、UDPはUNCONN(未接続)と明確に分かれます。UDPには「接続を待つ」という概念がないため、LISTENという状態が存在しないのです。

TCPで実際に接続が張られると、状態を持つことがさらにはっきりします。

$ ss -tan state all '( sport = :9000 )'
State  Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0      1            0.0.0.0:9000      0.0.0.0:*
ESTAB  0      0        172.31.0.10:9000  172.31.0.20:59048

接続元ごとにESTAB(確立済み)の行が増えていきます。TCPはLISTENSYN-SENTESTABLISHEDTIME-WAITなどの状態を遷移しますが、UDPにはこの状態遷移そのものがありません

なおss -uanでUDPソケットがESTABと表示されることがありますが、これは通信相手が確立したという意味ではありません。アプリケーションがそのソケットに宛先を固定した(connect()した)というローカルな状態を示しているだけで、ネットワーク上に接続が存在するわけではないので混同しないよう注意してください。

【要注意】UDPのポート開放確認は誤判定する

ここが実務で最もハマるポイントです。「ポートが開いているか」の確認方法は、TCPとUDPで信頼度がまったく違います。

TCPは明確に拒否される

何も待ち受けていない9999番へ接続してみます。

$ nc -zv -w 2 172.31.0.10 9999
nc: connect to 172.31.0.10 port 9999 (tcp) failed: Connection refused
終了ステータス: 1

このときサーバー側では、TCPがRSTフラグを立てたパケットを返しています。

172.31.0.20.36960 > 172.31.0.10.9999: Flags [S], seq 713531340, length 0
172.31.0.10.9999  > 172.31.0.20.36960: Flags [R.], ack 713531341, win 0, length 0

Connection refusedが出たら「サーバーには届いているが、そのポートで何も動いていない」と断定できます。TCPは閉じていることをプロトコル自身が教えてくれるわけです。

UDPは「閉じている」を伝える手段がない

同じ9999番にUDPで送ると、返ってくるのはUDPではなくICMPのport unreachableです。

172.31.0.20.60632 > 172.31.0.10.9999: UDP, length 1
172.31.0.10 > 172.31.0.20: ICMP 172.31.0.10 udp port 9999 unreachable, length 37

つまりUDPの疎通確認はICMPが返ってくることに依存しています。ところが、ICMPはセキュリティ上の理由でファイアウォールに落とされることが非常に多いプロトコルです。そこで、ICMPをブロックした状態で同じ確認をしてみます。

# サーバー側でICMPのport unreachableだけを落とす
iptables -A OUTPUT -p icmp --icmp-type port-unreachable -j DROP
$ nc -zvu -w 2 172.31.0.10 9999
Connection to 172.31.0.10 9999 port [udp/*] succeeded!
終了ステータス: 0

# 比較:同じ状況でTCPは正しく失敗する
$ nc -zv -w 2 172.31.0.10 9999
nc: connect to 172.31.0.10 port 9999 (tcp) failed: Connection refused
終了ステータス: 1

9999番では何ひとつ待ち受けていないのに、succeeded!と表示され、終了ステータスまで0(成功)になりました。UDPは応答が返らないのが正常な状態なので、「拒否されなかった=開いている」と判定するしかなく、ICMPが遮断されると開いているポートと区別がつかなくなるためです。

nmapがUDPスキャンの結果をopen|filteredという曖昧な表記で返すのも同じ理由です。UDPポートの疎通を確認したいときは、次のように進めてください。

  1. サーバー側でss -ulnを実行し、そのポートで待ち受けているプロセスが実在するか確認する(これが最も確実)
  2. サーバー側でtcpdump -i any -n "udp port ポート番号"を動かし、クライアントからのパケットが実際に到達しているか見る
  3. nc -zvuの成功だけを根拠にしない。そのサービスが理解できる本物のリクエストを送り、応答が返るかで判断する(DNSならdig、NTPならntpdate -qなど)

検証後は追加したルールを削除しておきます。

iptables -D OUTPUT -p icmp --icmp-type port-unreachable -j DROP

よくあるトラブルと切り分け手順

症状疑うところ確認コマンド
SSHがすぐConnection refusedTCP 22番でsshdが起動していないss -tlnp | grep :22
SSHが応答せずタイムアウト途中のファイアウォールで破棄されているtcpdump -i any -n "tcp port 22"
短い名前解決は通るが特定ドメインだけ失敗応答が大きくTCP 53番へ切り替わっているdig +tcp @DNSサーバー ドメイン
SNMPで機器を監視できないUDP 161番。ICMP遮断で確認も誤判定しやすい機器側でss -ulnp | grep :161
音声・映像が途切れるUDPのためロスがそのまま品質に出るping -c 100でロス率を測る
ファイル転送だけ極端に遅いTCPの再送。ロスがあると速度が落ちるss -tiretransを確認

切り分けの原則はシンプルです。TCPなら「拒否された(refused)」か「無反応(timeout)」かで、サービス停止とファイアウォール遮断を切り分けられます。UDPではこの区別ができないので、必ずサーバー側からsstcpdumpで確認してください。

TCP・UDPはTCP/IPのどこに位置するのか

TCPとUDPはどちらも、TCP/IPの4階層モデルにおけるトランスポート層のプロトコルです。その下のインターネット層をIPが担当し、上のアプリケーション層をHTTPやDNSが担当します。この位置関係が分かると、「なぜTCPだけが再送できるのか」「なぜどちらもポート番号を使うのか」が整理しやすくなります。

実測2で16進ダンプを層ごとに区切れたのも、この階層構造がそのままバイト列の並びになっているからです。階層全体の役割と、パケットロス環境での両者の比較はTCP/IPとは?4階層の役割と通信の流れをコマンド出力で解説にまとめています。20%のパケットロス下でTCPは完全なデータを届ける一方、UDPは200個中162個しか届かないという実測値も掲載しています。

TCPとUDPの使い分け

ここまでの実測を踏まえると、選択の基準は「速度か信頼性か」よりも「失われたデータをどう扱いたいか」で考えるほうが的確です。

データが失われたときにどうしたいか選ぶべきもの
時間がかかっても再送して完全に揃えたいTCPWeb・メール・SSH・ファイル転送・データベース
古いデータを再送されても意味がない。今の状態が欲しいUDP音声通話・ライブ配信・オンラインゲーム
失敗したらアプリ側で投げ直すほうが速いUDPDNS問い合わせ・SNMP・NTP
多数の相手へ一斉に送りたいUDPDHCP・マルチキャスト配信
信頼性は必要だが接続確立の待ち時間も惜しいUDP+QUICHTTP/3

「1秒前の音声が今さら届いても再生できない」——この一点がUDPを選ぶ最大の理由です。逆にファイル転送では1バイトの欠けも許されないため、TCPの再送待ちは必要なコストになります。

よくある質問

TCPとUDPはどちらが速いですか?

1回のやり取りにかかる時間はUDPのほうが短くなります。実測1のとおりTCPは接続確立と切断で余分に7パケットを使うためです。ただし回線の帯域そのものは変わらないため、大きなファイルを転送する速度がUDPで劇的に速くなるわけではありません。差が大きく出るのは、DNSのように小さなデータを1往復で終わらせる用途です。

同じポート番号をTCPとUDPで同時に使えますか?

使えます。実測でも9000番をTCPとUDPで同時に待ち受け、ss -tulnLISTENUNCONNの2行が並びました。ポート番号はプロトコルごとに独立した空間なので、TCP 53番とUDP 53番は別物として扱われます。ファイアウォールの設定でも、TCPとUDPは個別に許可する必要があります。

UDPはデータが壊れても分からないのですか?

チェックサムがあるため、壊れていること自体は検出できます。実測2のUDPヘッダにも587bというチェックサム欄がありました。ただし検出した後に再送を要求する仕組みがないため、壊れたデータは黙って破棄されます。「壊れたと気づけるが、直せない」というのが正確な理解です。

nc -zvu が成功したのにサービスに繋がりません

UDPの疎通確認は誤判定するためです。本記事で実測したとおり、ICMPが遮断されている環境では何も待ち受けていないポートでもsucceeded!と表示されます。サーバー側でss -ulnpを実行してプロセスが実在するか確認するか、そのサービスが解釈できる本物のリクエストを送って応答を確かめてください。

HTTP/3がUDPを使うのはなぜですか?

TCPの再送制御が、複数の通信を1本の接続にまとめたときに足を引っ張るためです(1つのパケットロスで全体が待たされる問題)。HTTP/3はUDPを土台にして、再送や順序制御をQUICというプロトコルで作り直し、影響範囲をストリームごとに限定しています。/etc/serviceshttps 443/udpが登録されているのはこのためです。

tcpdump を使うのに root 権限は必要ですか?

必要です。パケットキャプチャにはネットワークインターフェースを直接読む権限(CAP_NET_RAW)が要ります。本記事のDocker環境で--cap-add NET_RAWを付けているのはこのためです。本番サーバーで安易にキャプチャすると通信内容が平文で保存される点にも注意してください。検証は隔離環境で行うのが安全です。

まとめ

TCPとUDPの違いは、実際にパケットを見ると次の3点に集約されます。

  • 手続きの量が違う:同じ5バイトを送るのにTCPは8パケット549バイト、UDPは1パケット47バイト
  • ヘッダに持っている情報が違う:TCPヘッダ32バイトにはシーケンス番号・確認応答番号・ウィンドウサイズがあり、UDPヘッダ8バイトにはポート・長さ・チェックサムしかない。順序保証や再送ができるかどうかは、この欄の有無で決まっている
  • 確認方法の信頼度が違う:TCPはConnection refusedで閉じていると断定できるが、UDPはnc -zvuが誤って成功を返す

サーバー運用の現場では、まずss -tulnでTCPとUDPのどちらで待ち受けているかを確認し、疑わしいときはtcpdumpでパケットが本当に届いているかを見る、という順序で切り分けると迷いません。特にUDPは「応答がないのが正常」なので、クライアント側のツールの結果だけを信用しないことが重要です。

関連記事

Bash玄

はじめまして!Bash玄です。

エンジニアとしてシステム運用に携わる中で、手作業の多さに限界を感じ、Bashスクリプトを活用して業務を効率化したのがきっかけで、この道に入りました。「手作業は負け」「スクリプトはシンプルに」をモットーに、誰でも実践できるBashスクリプトの書き方を発信しています。

このサイトでは、Bashの基礎から実践的なスクリプト作成まで、初心者でもわかりやすく解説しています。少しでも「Bashって便利だな」と思ってもらえたら嬉しいです!

# 好きなこと
- シンプルなコードを書くこと
- コマンドラインを快適にカスタマイズすること
- 自動化で時間を生み出すこと

# このサイトを読んでほしい人
- Bashに興味があるけど、何から始めればいいかわからない人
- 定型業務を自動化したい人
- 効率よくターミナルを使いこなしたい人

Bashの世界に一歩踏み出して、一緒に「Bash道」を極めていきましょう!

Bash玄をフォローする