UDP通信¶
概要¶
UDP¶
UDPはUser Datagram Protocolの頭字語です。ソケットという用語を使っていますが、コネクションレスのプロトコルです。TCPに比べてオーバーヘッドが少なく効率のよい通信ですが、送信できるサイズに制約があり、トラフィック状況に合わせた送信調整や順序化、再送といった制御がありません。
JavaでUDPを扱うAPI¶
Javaでは、UDP特有のAPIとしてjava.netパッケージで提供されるもの、java.nioパッケージで提供されるものと大きく2種類があります。 基本は、Javaの当初のバージョンから提供されているjava.netパッケージです。世の中の大半のソケット・プログラミングの解説もこちらのパッケージについてがほとんどです。 java.netパッケージでは、以下のクラスが提供されています。
- java.net.DatagramSocket
- java.net.DatagramPacket 一方、JDK1.4から新たにjava.nio(Java New I/O)パッケージが搭載されました。これにより、従来のjava.netパッケージの機能に加え、バッファ管理、ノンブロッキング・モードと多重化などを提供するスケーラブルなI/O、文字セットなどの高度な機能が利用できるようになりました。
java.nioパッケージでは、以下のクラスが提供されています。
- java.nio.channels.DatagramChannel
- java.nio.ByteBuffer
UDPの送受信¶
UDP送信¶
基本的な手順は以下です。
- DatagramSocketの生成
- InetAddressまたはInetSocketAddressの生成
- DatagramPacketの生成
-
DatagramSocketのsendメソッドでDatagramPacketを送信
-
DatagramSocketの生成
1 2 3 4 | <pre> <code class="java"> socket = new DatagramSocket(); </code></pre> |
UDPの送信では、宛先アドレス・ポート番号はsocketではなくpacketに設定します。
- InetSocketAddressの生成
1 2 3 4 5 6 7 8 9 10 | <pre> <code class="java"> String address = "Adelphi"; int port = 1234; InetSocketAddress address = new InetSocketAddress(address, port); </code></pre> <pre> <code class="java"> InetAddress broadcastAddr= InetAddress.getByName("255.255.255.255"); </code></pre> |
InetSocketAddressは、ホスト名とポート番号を保持するクラスです。InetAddressの場合はポート番号を持たないので、後述のDatagramPacket生成時にポート番号を指定する必要があります。 ホスト名には、実行するマシンでIPアドレス解決可能な文字列、あるいはIPアドレスの文字列表現を指定することができます。IPv6アドレスも対応しています。
- DatagramPacketの生成
1 2 3 4 5 | <pre> <code class="java"> byte[] sendData = ... DatagramPacket packet = new DatagramPacket(sendData, 0, sendData.length, address); </code></pre> |
sendDataはbyte[]型です。byte[]に直接データを詰めるのは大変なので、ByteArrayOutputStreamやByteBufferを使用するのが便利です。
- DatagramSocketの送信
1 2 3 4 | <pre> <code class="java"> socket.send(packet); </code></pre> |
UDP受信¶
UDP受信の基本的な手順は以下です
- DatagramSocketの生成
- DatagramPacketの生成
-
DatagramSocketのreceiveメソッドでDatagramPacketに受信データを詰める
-
DatagramSocketの生成
1 2 3 4 5 | <pre> <code class="java"> int port = 1234; socket = new DatagramSocket(port); </code></pre> |
受信で使用する場合は、ソケット生成時にポート番号を指定します。 複数のアドレス(ネットワーク・インタフェース)を持つマシンでは、すべてのアドレスで上記ポート番号でリッスンします。特定のアドレスだけで受信させるようにすることも可能です。その場合には、コンストラクタでローカルのアドレスを指定します。
- DatagramPacketの生成
1 2 3 4 5 | <pre> <code class="java"> byte[] array = ... DatagramPacket packet = new DatagramPacket(array, array.length); </code></pre> |
- DatagramSocketの受信
1 2 3 4 | <pre> <code class="java"> socket.receive(packet); </code></pre> |
UDPパケットを受信します。受信するまでブロックします。
データの作成¶
送信するデータは、byte型の配列で用意する必要があります。受信データはbyte型の配列に格納されます。 しかし、通常送受信するデータは文字列や数値型などであり、byte型の配列に格納するのは少々面倒です。
文字列(String)をbyte配列へ詰める¶
StringクラスのgetBytesメソッドが利用できます。 引数に文字セット(例:"UTF-8")を指定する必要があります。 送受信する両者で同一の文字セットを使用します。 文字セットとして指定できる一覧は、java.nio.charset.CharsetクラスのメソッドavailableCharsetsで取得できます。
整数型(int)をbyte配列へ詰める¶
ByteArrayOutputStreamとDataOutputStreamを組み合わせて使用する方法、 ByteBufferを使用する方法、BigIntegerを使用する方法などがあります。 DataOutputStreamはintの格納はBig Endianのみです。 ByteBufferはデフォルトはBig EndianですがorderメソッドでLittle Endianに変更することができます。 BigIntegerはBig Endianのみです。
文字列をbyte配列に詰める¶
1 2 3 4 5 | <pre> <code class="java"> String message = "EHLO pisa\r\n"; byte[] array = message.getBytes("UTF-8"); </code></pre> |
int型をbyte配列に詰める~ByteArrayOutputStreamとDataOutputSteam¶
1 2 3 4 5 6 7 8 | <pre> <code class="java"> ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); dos.writeInt(value); dos.flush(); byte[] array = baos.toByteArray(); </code></pre> |
int型をbyte配列に詰める~ByteBuffer¶
1 2 3 4 5 6 | <pre> <code class="java"> ByteBuffer buffer = ByteBuffer.allocate(1024); // 最大サイズ 1024byte buffer.putInt(value); byte[] array = buffer.array(); </code></pre> |
int型をbyte配列に詰める~BigInteger¶
1 2 3 4 5 | <pre> <code class="java"> BigInteger intValue = BigInteger.valueOf(value); byte[] array = intValue.toByteArray(); </code></pre> |