概要

UDP は TCP と異なり、接続を確立せずにデータグラム(パケット)を送信するプロトコルです。到着順序やパケットロスの保証がない代わりに、オーバーヘッドが小さく高速に通信できる特徴があります。業務システムでは syslog やメトリクス送信、DNS クエリなどの場面で使われています。Java では DatagramSocket と DatagramPacket を使うことで、UDP の送受信を標準 API だけで実装できます。この記事では、送信側と受信側の基本パターンから、パケットサイズの制約、文字コードの指定、Java 21 での仮想スレッドとの組み合わせまで、実務で必要な知識を整理します。

使いどころ

アプリケーションのメトリクスデータを Graphite や StatsD サーバーに UDP で送信する

社内ネットワークの監視エージェントから中央サーバーにログを UDP で配信する

軽量な通知プロトコルを UDP で実装し、サービス間のイベント通知を低遅延で行う

コード例

UDP データグラムの送信と受信
import java.io.*;

public class UdpSocketSample {
    private static final int PORT = 9001;
    private static final String HOST = "localhost";

    record DatagramMessage(String text, InetAddress from, int port) {}

    public static void startReceiver(ExecutorService executor) throws Exception {
        var receiverSocket = new DatagramSocket(PORT);
        executor.submit(() -> {
            try {
                var buffer = new byte[1024];
                var packet = new DatagramPacket(buffer, buffer.length);
                receiverSocket.receive(packet);

                var msg = new DatagramMessage(
                    new String(packet.getData(), 0, packet.getLength(), "UTF-8"),
                    packet.getAddress(),
                    packet.getPort()
                );
                System.out.printf("受信: %s (from %s:%d)%n",
                    msg.text(), msg.from(), msg.port());
                receiverSocket.close();
            } catch (IOException e) {

            }
        });
    }

    public static void sendMessage(String message) throws IOException {
        try (var senderSocket = new DatagramSocket()) {
            var data = message.getBytes("UTF-8");
            var address = InetAddress.getByName(HOST);
            var packet = new DatagramPacket(data, data.length, address, PORT);
            senderSocket.send(packet);
            System.out.println("送信: " + message);
        }
    }

    public static void main(String[] args) throws Exception {
        var executor = Executors.newSingleThreadExecutor();
        startReceiver(executor);
        Thread.sleep(100);
        sendMessage("ログメッセージ: サービス起動完了");
        executor.awaitTermination(2, TimeUnit.SECONDS);
        executor.shutdown();
    }
}

Java 8 / 17 / 21 の完全なサンプルコードは GitHub リポジトリ で確認できます。

Version Coverage

record でデータグラムのメタ情報を構造化し、var で記述を簡潔にできる。テキストブロックで受信結果を整形できる。

Java 17
// Java 17: record + var で受信データを構造化
record DatagramMessage(String text,
        InetAddress from, int port) {}
var buf = new byte[1024];
var pkt = new DatagramPacket(buf, buf.length);
socket.receive(pkt);
var msg = new DatagramMessage(
    new String(pkt.getData(), 0,
        pkt.getLength(), "UTF-8"),
    pkt.getAddress(), pkt.getPort());
System.out.printf("""
    受信: %s (from %s:%d)%n""",
    msg.text(), msg.from(), msg.port());

Library Comparison

標準 API(DatagramSocket)UDP 通信の学習や、軽量なメトリクス送信・ログ配信。依存なしで動作する。信頼性保証や再送制御は自前で実装する必要がある。
Netty(UDP チャネル)高スループットの UDP サーバーを構築する場合。非同期 I/O で多数のパケットを効率的に処理できる。学習コストが高い。小規模な用途にはオーバースペック。

注意点

UDP はパケットの到達を保証しない。重要なデータの送信には TCP を使うか、アプリケーション層で再送制御を実装すること

DatagramPacket のバッファサイズを超えるデータは切り捨てられる。受信側のバッファサイズを十分に確保すること

UDP のペイロード最大サイズは 65,507 バイトだが、MTU(通常 1,500 バイト)を超えるとフラグメント化が起こり、パケットロスの確率が上がる

ファイアウォールで UDP ポートがブロックされている場合が多い。ネットワーク管理者に確認すること

FAQ

UDP でデータが届かない場合の検知方法はありますか。

UDP 自体にはパケットロスの検知機能がありません。アプリケーション層でシーケンス番号を付与し、受信側で欠番を検知する方法が一般的です。

マルチキャスト送信はできますか。

MulticastSocket を使えば、複数の受信者にデータグラムを同時送信できます。受信側は指定のグループアドレスに joinGroup() で参加します。

DatagramPacket のバッファサイズはどのくらいに設定すべきですか。

送信データの最大サイズに合わせて設定します。一般的にはフラグメント化を避けるため 1,400 バイト以下に抑えるのが安全です。

関連書籍

この記事のテーマをさらに深く学びたい方へ。

※ Amazon アソシエイトリンクを含みます