【入門編】 UDPヘッダー構造(送信元/宛先ポート、長さ、チェックサム) – ネットワーク基礎とWebセキュリティ実践ガイド

こんにちは!ネットワークの裏側を覗くのが大好きな、技術メディアの主筆ライターです。

インフラやネットワークの世界へようこそ!
TCP/IPやOSI参照モデルといった言葉を聞くだけで、「なんだか難しそう……」「英語の専門用語ばかりで頭がパンクしそう……」と、そっとブラウザを閉じたくなる瞬間、ありますよね。すごくよく分かります。私も最初の頃は、ルーターやパケットという目に見えないもの相手に途方に暮れていました。

でも、安心してください!一歩ずつ、私たちの身近な世界に置き換えて紐解いていけば、ネットワークの仕組みは驚くほどスッキリと見えてきます。

今回は、TCPの「しっかり者のお手紙(確認応答あり)」とは対照的な、「とにかく速さ重視!細かいことは気にしない!」という自由奔放な性格の持ち主、UDP(User Datagram Protocol)の世界へ皆さんをご案内します。その心臓部である「UDPヘッダー」の構造を、一緒に楽しく解き明かしていきましょう!

—

1. 郵便配達でイメージするUDPの基本

まずは、UDPがどんな通信をしているのか、現実世界の「郵便配達」に例えて考えてみましょう。

TCPというプロトコルが、相手が確実に受け取ったことをサイン(受領印)してもらう「書留郵便」や「宅急便」だとすれば、UDPは街角の「ポスティング(チラシ投函)」です。
差出人は、チラシをせっせと宛先のポストに放り込みます。相手が留守だろうが、ポストがいっぱいだろうが、そんなことはお構いなし。配達員は投げ入れたらもう次の現場へダッシュです。「ちゃんと届いた?」という確認の連絡(ハンドシェイク)もしなければ、途中で風に飛ばされて失くなっても気づきません。

「えっ、それじゃあ困るじゃない!」って思いますよね?
でも、考えてみてください。ライブ配信の映像やオンラインゲームの位置情報、ボイスチャットはどうでしょう? 1フレーム分の映像がコンマ数秒遅れて届くくらいなら、前の映像をそのまま流して「今すぐ最新のデータをくれ!」と急ぐほうがありがたいですよね。UDPは、「多少のデータの取りこぼしよりも、スピードとリアルタイム性が命!」という場面で大活躍する、超重要人物なのです。

—

2. たったの8バイト!超軽量なUDPヘッダーの構造

さて、そんなUDPさんが荷物(データ)にペタッと貼り付ける「荷札」が、UDPヘッダーです。

TCPのヘッダーが色々な確認事項を書くために20バイト以上もあるのに対し、UDPの荷札はたったの8バイト(64ビット)しかありません。驚くほどミニマルですよね。
この小さな荷札の中身は、綺麗さっぱり以下の4つの情報だけで構成されています。

1. 送信元ポート番号(16ビット)
2. 宛先ポート番号(16ビット)
3. 長さ(16ビット)
4. チェックサム(16ビット)

「16ビットとか言われてもピンとこないよ!」という方のために、身近な例で一つずつ優しく解きほぐしていきましょう。

—

3. 4つのフィールドを解剖する!

① 送信元ポート番号 & ② 宛先ポート番号(各16ビット / 計4バイト)

  • 身近な例え: 手紙の「差出人の差出人部屋番号」と「宛先の部屋番号」

マンションにたくさんの人が住んでいるように、1台のパソコンの中でも、ブラウザ、Discord、オンラインゲームなど、たくさんのアプリが同時に動いていますよね。
パケットがパソコンに届いたとき、「これはどのアプリ宛の荷物なんだい?」と振り分けるための部屋番号が「ポート番号」です。

  • 送信元ポート番号: 「誰から送られたものか(例: アプリ側の臨時窓口)」
  • 宛先ポート番号: 「どこへ届ければいいか(例: Webサーバーなら 53 番や 443 番など)」

この2つがあるおかげで、届いたデータが迷子にならずに正しいアプリの扉をノックできるというわけです。

③ 長さ(16ビット / 2バイト)

  • 身近な例え: 荷札に書かれた「この荷物、全体で何グラムあるか」

UDPヘッダー自体の8バイトを含めて、このパケット(UDPデータグラム)の全体が何バイトあるのかを示す数字です。
「おいおい、中身はどこまでだ?」と受信側が混乱しないための、いわば荷物の総重量ラベルですね。これがあることで、受信側は「ここからここまでが今回のデータだな」と正確に切り取ることができます。

④ チェックサム(16ビット / 2バイト)

  • 身近な例え: 宛先の宛名が雨で滲んでいないか、途中で破れていないかを確かめる「検品スタンプ」

「UDPは信頼性がない(確認応答をしない)」と言いますが、まったくノーチェックかというと、実はそうではありません。この「チェックサム」という最小限の防衛ラインが用意されています。

データがネットワークの荒波を渡ってくる間に、ノイズなどで「0」と「1」のビットがひっくり返ってしまうことがあります。受信した側は、このチェックサムの計算結果を照らし合わせて、「あれ、この荷物、途中で中身がぐちゃぐチャに壊れてるぞ!」ということに気づくことができます。

ただし、ここで注意!
壊れていることに気づいたとしても、UDPは「あ、壊れてるわ。まぁいいか、ポイッ!」とデータを捨てるだけです。「ごめん、もう一回送って!」と相手に再送要求(TCPのような機能)はしてくれません。あくまで「壊れた偽物をうっかり受け取って誤作動を起こさないための安全装置」という立ち位置なんですね。

—

4. 実務で触れるUDP!Pythonでのシンプルな実装例

「理屈は分かったけれど、実際の開発現場ではどうやって動いているの?」
そんな疑問に答えるべく、Pythonを使って超シンプルなUDPの「送信プログラム(クライアント)」と「受信プログラム(サーバー)」のコードを見てみましょう。

実務のスクリプトでもよく使われるソケット通信の基本形です。日本語のコメントを丁寧に挟みましたので、ぜひ雰囲気を味わってみてください。

UDPサーバー側(待ち受け)

import socket

# IPv4 (AF_INET) と UDP (SOCK_DGRAM) を指定してソケットを作成します
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 自分のIPアドレスと、待ち受けるポート番号(ここでは 12345番)をバインド(紐付け)します
# 空の文字列 '' は「このマシンのすべてのIPインターフェースで待ち受ける」という意味になります
server_address = ('127.0.0.1', 12345)
server_socket.bind(server_address)

print("--- UDPサーバーが起動しました。データを持っています! ---")

try:
    while True:
        # 客户端からデータを受信します(一度に受け取る最大サイズは1024バイト)
        # 返回値として「データ(bytes型)」と「送信元のアドレス(IP, ポート)」が返ってきます
        data, client_address = server_socket.recvfrom(1024)
        
        print(f"受信成功! 宛先:{client_address} からのメッセージ: {data.decode('utf-8')}")
        
        # UDPなので「届いたよ!」という返事は自動では返しませんが、
        # 必要であればサーバー側から自主的に返信を送ることも可能です
        reply_message = "サーバー側でデータを受け取りました!"
        server_socket.sendto(reply_message.encode('utf-8'), client_address)

except KeyboardInterrupt:
    print("\nサーバーを終了します。お疲れ様でした!")
finally:
    server_socket.close()

UDPクライアント側(送信)

import socket

# UDP用のソケットを生成します
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 送信先のサーバー情報(IPアドレスとポート番号)
server_address = ('127.0.0.1', 12345)

message = "こんにちは、UDPの世界!スピード重視で飛ばします!"

try:
    # TCPのような事前のコネクション確立(3wayハンドシェイク)は一切なし!
    # いきなり宛先に向かってデータを放り投げます(sendto)
    print(f"送信中... -> {server_address}")
    client_socket.sendto(message.encode('utf-8'), server_address)
    
    # サーバーからの返信を待ち受ける(※UDPなので、サーバーが返信を返さなければここで止まります)
    client_socket.settimeout(3.0) # 3秒でタイムアウトを設定
    data, server_address = client_socket.recvfrom(1024)
    print(f"サーバーからの返信: {data.decode('utf-8')}")

except socket.timeout:
    print("タイムアウトしました:サーバーからの返信がありません(UDPなので届いたか分かりません!)")
finally:
    client_socket.close()

このコードを動かしてみると、TCPのように「接続を確立してからデータを送る」という前段階の儀式が一切なく、「思い立ったが吉日、すぐに投げる」というUDPの軽快なスピード感を肌で感じることができるはずです。

—

5. まとめ

いかがでしたでしょうか?
今回は、UDPヘッダーの構造とその裏側にある哲学を、郵便配達や身近な例えを交えて紐解いてみました。

  • UDPヘッダーはたったの8バイトという驚異の軽さ!
  • 送信元ポート・宛先ポートで、どのアプリ宛の荷物か迷子を防ぐ。
  • 長さとチェックサムで最低限のサイズ確認と破損チェックを行う。
  • ただし、届いたかどうかの確認や壊れたときの再送は一切しない「スピード特化型」の自由なやつ!

ネットワークやセキュリティの世界は、一見すると難解なアルゴリズムの塊に見えますが、その根底にあるのは「いかに効率よく、正確に、あるいは素早く情報を伝えるか」という人間社会の知恵の延長線上にあります。

今回の解説が、皆さんのインフラ学習や日々の開発業務のモヤモヤをスッキリ解消するきっかけになれば、ライターとしてこれ以上の喜びはありません。
それではまた、次回のネットワーク探検でお会いしましょう!

コメント

タイトルとURLをコピーしました