概要
UDP は TCP と異なり、接続を確立せずにデータグラム(パケット)を送信するプロトコルです。到着順序やパケットロスの保証がない代わりに、オーバーヘッドが小さく高速に通信できる特徴があります。業務システムでは syslog やメトリクス送信、DNS クエリなどの場面で使われています。Java では DatagramSocket と DatagramPacket を使うことで、UDP の送受信を標準 API だけで実装できます。この記事では、送信側と受信側の基本パターンから、パケットサイズの制約、文字コードの指定、Java 21 での仮想スレッドとの組み合わせまで、実務で必要な知識を整理します。
使いどころ
アプリケーションのメトリクスデータを Graphite や StatsD サーバーに 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();
}
}Version Coverage
record でデータグラムのメタ情報を構造化し、var で記述を簡潔にできる。テキストブロックで受信結果を整形できる。
// 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
注意点
UDP はパケットの到達を保証しない。重要なデータの送信には TCP を使うか、アプリケーション層で再送制御を実装すること
DatagramPacket のバッファサイズを超えるデータは切り捨てられる。受信側のバッファサイズを十分に確保すること
UDP のペイロード最大サイズは 65,507 バイトだが、MTU(通常 1,500 バイト)を超えるとフラグメント化が起こり、パケットロスの確率が上がる
ファイアウォールで UDP ポートがブロックされている場合が多い。ネットワーク管理者に確認すること
FAQ
UDP 自体にはパケットロスの検知機能がありません。アプリケーション層でシーケンス番号を付与し、受信側で欠番を検知する方法が一般的です。
MulticastSocket を使えば、複数の受信者にデータグラムを同時送信できます。受信側は指定のグループアドレスに joinGroup() で参加します。
送信データの最大サイズに合わせて設定します。一般的にはフラグメント化を避けるため 1,400 バイト以下に抑えるのが安全です。