UDP 通信プログラムに必要な2つのクラス:
1. DatagramSocket
2. DatagramPacket
TCP と UDP の大きな違いは:
TCP は固定的な接続を確立しなければならない
UDP は固定的な接続が必要ない
図で表すと,
TCP 通信は, 電話のように事前のコネクションを確立し, 入出力をストリームとして取り扱う.
UDP 通信は, 個々の封筒 (パケット) に毎回宛先を書いてデータを送信する.
UDP 通信では固定的なコネクションを必要としないため, ローカルのポートにさえバインドされていれば通信を行うことができる.
ローカルホストのポートにバインドされた DatagramSocket のインスタンスを作成するには, 次のコンストラクタを用いる:
1. public DatagramSocket(); // 自動的に割り振られたアドレスにバインドされる
2. public DatagramSocket(int port);
3. public DatagramSocket(int port, InetAddress laddr);
4. public DatagramSocket(SocketAddress bindaddr);
DatagramPacket は, DatagramSocket を通して送受信される UDP パケットを表すクラス.
DatagramPacket は主に次のような情報を保持している:
1. データの通信相手のソケットアドレス (IP アドレス + ポート番号)
2. データのバイト列
DatagramPacket クラスには,
送信用のパケットを作成するためのコンストラクタは次の4つがある:
1. public DatagramPacket(byte buf[], int length, InetAddress addr, int port);
2. public DatagramPacket(byte buf[], int offset, int length, InetAddress addr, int port);
3. public DatagramPacket(byte buf[], int length, SocketAddress addr);
4. public DatagramPacket(byte buf[], int offset, int length, SocketAddress addr);
受信用のパケットを作成するためのコンストラクタは次の2つがある:
1. public DatagramPacket(byte buf[], int length);
2. public DatagramPacket(byte buf[], int offset, int length);
UDP を利用した通信プログラムを作成しよう. ここでは, 一方向 にメッセージを送信するだけのプログラム.
まず送信側のプログラムを示す.
DatagramSender.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | import java.net.DatagramSocket;
import java.net.DatagramPacket;
import java.net.InetSocketAddress;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
public class DatagramSender{
public static final int SERVER_PORT = 51015;
public static final int PACKET_SIZE = 1024;
public static void main(String[] args){
DatagramSocket socket = null;
/*
パケットの宛先に利用するソケットアドレスを設定している.
引数: IP アドレス, 51015
*/
InetSocketAddress remoteAddress = new InetSocketAddress(args[0], SERVER_PORT);
try{
BufferedReader keyIn = new BufferedReader(new InputStreamReader(System.in));
/*
パケットの送信に利用する DatagramSocket を作成している.
ポート番号は自動的に割り振られる.
*/
socket = new DatagramSocket();
String message;
while( (message = keyIn.readLine()).length() > 0 ){
byte[] buf = message.getBytes();
/*
送信するパケットを作成している.
まず, キーボードから入力された文字列 byte 型の配列に変換し,
そのデータを用いてパケットを作成している.
*/
DatagramPacket packet = new DatagramPacket(buf, buf.length, remoteAddress);
/*
作成したパケットを送信する.
*/
socket.send(packet);
}
}catch(IOException e){
e.printStackTrace();
}finally{
if(socket != null){
socket.close();
}
}
}
}
|
次は受信側のプログラムを示す.
DatagramReceiver.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | import java.net.DatagramSocket;
import java.net.DatagramPacket;
import java.io.IOException;
public class DatagramReceiver{
public static final int SERVER_PORT = 51015;
public static final int PACKET_SIZE = 1024;
public static void main(String args[]){
DatagramSocket socket = null;
/*
受信パケットの入れ物を作成している.
まず, byte 型の配列を準備し, その配列を引数にして DatagramPacket のコンストラクタを呼び出している.
*/
byte[] buf = new byte[PACKET_SIZE];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
try{
/*
パケットの送受信に用いるソケットを作成している.
送信側と同じ 51015 ポートを利用している.
*/
socket = new DatagramSocket(SERVER_PORT);
System.out.println("DatagramReceiver が起動した, port = " + socket.getLocalPort());
while(true){
/*
パケットの到着を待っている.
*/
socket.receive(packet);
/*
受信したパケットから文字列を取り出している.
byte 配列から文字列を作成する String クラスのコンストラクタを利用している.
*/
String message = new String(buf, 0, packet.getLength());
/*
受信した文字列を, 送信元のアドレスとともに表示している.
送信元のアドレスは, DatagramPacket クラスの getSocketAddress メソッドで取得することができる.
*/
System.out.println(packet.getSocketAddress() + " 受信: " + message);
}
}catch(IOException e){
e.printStackTrace();
}finally{
if(socket != null){
socket.close();
}
}
}
}
/*
注意:
このプログラムは前節のMultiEchoServer のようにマルチスレッド
にしていないが, 複数のクライアントからの接続を同時に処理する
ことができる.
DatagramSocket は固定的な接続を必要としないため, ソケット自体
にはなにも変更を加えることなく, 1つのソケットで複数の相手と
通信することができる.
*/
|
DatagramSender.java (送信側のプログラムの実行結果):
[wtopia]$ java DatagramSender 127.0.0.1
hello_1
hello_2
hello_3
DatagramReceiver.java (受信側のプログラムの実行結果):
[wtopia]$ java DatagramReceiver [~/src_py/sphinx/java.net/SenderReceiver]
DatagramReceiver が起動した, port = 51015
/127.0.0.1:57110 受信: hello_1
/127.0.0.1:57110 受信: hello_2
/127.0.0.1:57110 受信: hello_3
サンプルの構成:
1. DatagramDaytimeServer.java
2. DatagramDaytimeClient.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.util.Date;
public class DatagramDaytimeServer{
public static final int DAYTIME_PORT = 51015;
public static final int PACKET_SIZE = 1024;
public static void main(String[] args){
DatagramSocket socket = null;
byte[] buf = new byte[PACKET_SIZE];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
try{
socket = new DatagramSocket(DAYTIME_PORT);
System.out.println("DatagramDaytimeServer が起動した, port = " + socket.getLocalPort());
while(true){
socket.receive(packet);
// 時刻を求める
String daytime = (new Date()).toString();
byte[] bufs = daytime.getBytes();
// 送信データグラムインスタンスの作成
DatagramPacket sendPacket = new DatagramPacket(bufs, bufs.length, packet.getSocketAddress());
// データグラムパケットの送信
socket.send(sendPacket);
}
}catch(IOException e){
e.printStackTrace();
}finally{
if(socket != null){
socket.close();
}
}
}
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | import java.io.IOException;
import java.net.DatagramSocket;
import java.net.DatagramPacket;
import java.net.InetSocketAddress;
import java.net.SocketTimeoutException;
public class DatagramDaytimeClient{
public static final int DAYTIME_PORT = 51015;
public static final int PACKET_SIZE = 1024;
public static void main(String[] args){
DatagramSocket socket = null;
InetSocketAddress remoteAddress;
// 受信データグラムインスタンスの作成
byte[] buf = new byte[PACKET_SIZE];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
// 引数で指定したサーバに接続する
if(args.length == 0){
System.out.println("引数に接続先のサーバを指定してください");
return;
}else if(args.length == 1){
System.out.println("デフォルトのポート番号を使用する");
remoteAddress = new InetSocketAddress(args[0], DAYTIME_PORT);
}else{
remoteAddress = new InetSocketAddress(args[0], Integer.parseInt(args[1]));
}
try{
socket = new DatagramSocket();
// 5 秒以内に反応がない場合エラー出力する
socket.setSoTimeout(5000);
// パケットを送る
byte[] bufs = "".getBytes();
DatagramPacket sendPacket = new DatagramPacket(bufs, bufs.length, remoteAddress);
// データグラムパケットの送信
socket.send(sendPacket);
// データグラムパケットの受信
socket.receive(packet);
// 受信データグラムパケットの内容
String daytime = new String(buf, 0, packet.getLength());
System.out.println(daytime);
}catch(SocketTimeoutException e){
System.out.println("サーバから反応がない");
}catch(IOException e){
e.printStackTrace();
}finally{
if(socket != null){
socket.close();
}
}
}
}
|
DatagramDaytimeServer.java の実行結果:
[wtopia]$ java DatagramDaytimeServer
DatagramDaytimeServer が起動した, port = 51015
DatagramDaytimeClient.java の実行結果:
[wtopia UDP]$ java DatagramDaytimeClient
引数に接続先のサーバを指定してください
[wtopia UDP]$ java DatagramDaytimeClient 127.0.0.1
デフォルトのポート番号を使用する
Sat Aug 27 15:40:28 JST 2011
[wtopia UDP]$ java DatagramDaytimeClient 127.0.0.1
デフォルトのポート番号を使用する
Sat Aug 27 15:40:36 JST 2011