1. 概要

このチュートリアルでは、UDPパケットの送受信を可能にするDatagramChannelクラスについて説明します。

2. DatagramChannel

インターネットでサポートされているさまざまなプロトコルの中で、TCPとUDPが最も一般的です。

TCPはコネクション型プロトコルですが、 UDPはデータグラム指向のプロトコルであり、パフォーマンスが高く、信頼性が低くなります。 UDPは、信頼性が低いため、ブロードキャストまたはマルチキャストデータ送信の送信によく使用されます

JavaのNIOモジュールDatagramChannelクラスは、データグラム指向ソケット用の選択可能なチャネルを提供します。 つまり、データグラム(UDPパケット)を送受信するためのデータグラムチャネルを作成できます。

DatagramChannel クラスを使用して、ローカルIPアドレスを介してデータグラムを送信するクライアントとデータグラムを受信するサーバーを作成してみましょう。

3. 開いてバインドする

まず、開いているが接続されていないデータグラムチャネルを提供する openChannel メソッドを使用して、DatagramChannelBuilderクラスを作成しましょう。

public class DatagramChannelBuilder {
    public static DatagramChannel openChannel() throws IOException {
        DatagramChannel datagramChannel = DatagramChannel.open();
        return datagramChannel;
    }
}

次に、受信したUDPパケットをリッスンするために、開いているチャネルをローカルアドレスにバインドする必要があります。

そこで、DatagramChannelを指定されたローカルアドレスにバインドするbindChannelメソッドを追加します。

public static DatagramChannel bindChannel(SocketAddress local) throws IOException {
    return openChannel().bind(local); 
}

今、私たちは使用することができます DatagramChannelBuilder 構成されたソケットアドレスでUDPパケットを送受信するクライアント/サーバーを作成するクラス。

4. クライアント

まず、DatagramChannelBuilderクラスのすでに説明したbindChannelメソッドを使用するstartClientメソッドを使用して、 DatagramClient クラスを作成しましょう:

public class DatagramClient {
    public static DatagramChannel startClient() throws IOException {
        DatagramChannel client = DatagramChannelBuilder.bindChannel(null);
        return client;
    }
}

クライアントはインバウンドUDPパケットをリッスンする必要がないため、チャネルのバインド中にアドレスにnull値を指定しました。

次に、 sendMessage メソッドを追加して、サーバーアドレスでデータグラムを送信します。

public static void sendMessage(DatagramChannel client, String msg, SocketAddress serverAddress) throws IOException {
    ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
    client.send(buffer, serverAddress);
}

それでおしまい! これで、クライアントを使用してメッセージを送信する準備が整いました。

DatagramChannel client = startClient();
String msg = "Hello, this is a Baeldung's DatagramChannel based UDP client!";
InetSocketAddress serverAddress = new InetSocketAddress("localhost", 7001);

sendMessage(client, msg, serverAddress);

注:メッセージを localhost:7001 アドレスに送信したため、同じアドレスを使用してサーバーを起動する必要があります。

5. サーバ

同様に、startServerメソッドを使用してDatagramServerクラスを作成し、 localhost:7001アドレスでサーバーを起動します。

public class DatagramServer {
    public static DatagramChannel startServer() throws IOException {
        InetSocketAddress address = new InetSocketAddress("localhost", 7001);
        DatagramChannel server = DatagramChannelBuilder.bindChannel(address);
        System.out.println("Server started at #" + address);
        return server;
    }
}

次に、クライアントからデータグラムを受信し、メッセージを抽出して出力するreceiveMessageメソッドを追加しましょう。

public static void receiveMessage(DatagramChannel server) throws IOException {
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    SocketAddress remoteAdd = server.receive(buffer);
    String message = extractMessage(buffer);
    System.out.println("Client at #" + remoteAdd + "  sent: " + message);
}

また、受信したバッファからクライアントのメッセージを抽出するには、extractMessageメソッドを追加する必要があります。

private static String extractMessage(ByteBuffer buffer) {
    buffer.flip();
    byte[] bytes = new byte[buffer.remaining()];
    buffer.get(bytes);
    String msg = new String(bytes);
    
    return msg;
}

ここでは、ByteBufferインスタンスでflipメソッドを使用して、I/Oからの読み取りからI/Oへの書き込みに切り替えました。 さらに、 flip メソッドは、制限を現在の位置に設定し、位置をゼロに設定して、最初から読み取れるようにします。

これで、サーバーを起動して、クライアントからメッセージを受信できます。

DatagramChannel server = startServer();
receiveMessage(server);

したがって、サーバーがメッセージを受信したときの印刷出力は次のようになります。

Server started at #localhost/127.0.0.1:7001
Client at #/127.0.0.1:52580  sent: Hello, this is a Baeldung's DatagramChannel based UDP client!

6. DatagramChannelUnitTest

クライアントとサーバーの両方の準備ができたので、単体テストを記述して、エンドツーエンドのデータグラム(UDPパケット)配信を検証できます。

@Test
public void whenClientSendsAndServerReceivesUDPPacket_thenCorrect() throws IOException {
    DatagramChannel server = DatagramServer.startServer();
    DatagramChannel client = DatagramClient.startClient();
    String msg1 = "Hello, this is a Baeldung's DatagramChannel based UDP client!";
    String msg2 = "Hi again!, Are you there!";
    InetSocketAddress serverAddress = new InetSocketAddress("localhost", 7001);
    
    DatagramClient.sendMessage(client, msg1, serverAddress);
    DatagramClient.sendMessage(client, msg2, serverAddress);
    
    assertEquals("Hello, this is a Baeldung's DatagramChannel based UDP client!", DatagramServer.receiveMessage(server));
    assertEquals("Hi again!, Are you there!", DatagramServer.receiveMessage(server));
}

まず、データグラムチャネルをバインドして、 localhost:7001の受信メッセージをリッスンするサーバーを起動しました。 次に、クライアントを起動して2つのメッセージを送信しました。

最後に、サーバーで受信メッセージを受信し、クライアントを介して送信したメッセージと比較しました。

7. 追加の方法

これまで、DatagramChannelクラスによって提供されるopen、bind、send、receiveなどのメソッドを使用してきました。 それでは、他の便利な方法を簡単に見ていきましょう。

7.1. configureBlocking

デフォルトでは、データグラムチャネルはブロックされています。 configureBlocking メソッドを使用して、false値を渡すときにチャネルを非ブロッキングにすることができます。

client.configureBlocking(false);

7.2. isConnected

isConnected メソッドは、データグラムチャネルのステータス、つまり、接続されているか切断されているかを返します。

7.3. ソケット

socket メソッドは、データグラムチャネルに関連付けられたDatagramSocketクラスのオブジェクトを返します。

7.4. 閉じる

さらに、DatagramChannelクラスのcloseメソッドを呼び出すことにより、チャネルを閉じることができます。

8. 結論

このクイックチュートリアルでは、UDPパケットを送受信するためのデータグラムチャネルの作成を可能にするJavaNIOのDatagramChannelクラスについて説明しました。

最初に、openbindのように、データグラムチャネルがインバウンドUDPパケットを同時にリッスンできるようにするいくつかの方法を検討しました。

次に、クライアントとサーバーを作成し、DatagramChannelクラスを使用してエンドツーエンドのUDPパケット配信を調査しました。

いつものように、ソースコードはGitHubから入手できます。