UDP


UDP (User Datagram Protocol) は次の2つの機能をもつだけのシンプルなプロトコルです。

UDP は TCPとは異なり、接続(Connection)を確立しなくても使用することができます。 UDPのデータ (データグラム, datagram と呼ばれる)は、個別のアドレス情報を保持しており、互いに独立している。 UDPソケットを作成すれば、どのようなアドレスのホストからでも、複数のホストとの間で次々にデータグラムを受信できます。 受信データグラムには送信元のIPアドレスやポート番号が含まれており、送られてきたデータの単位で取り出すことができ、TCPのようにデータがつながってしまうことはありません。つまり、UDPはメッセージの境界を保持します。

TCPとは異なり、UDPではデータグラムが届くかどうかは保証されず、また、データの届く順番も保証されません。

UDPでは、接続を確立せずに使えるので、TCPと比べると効率がよいといえます。 わずかな量のデータしかやりとりしない場合は、TCPの接続確立のためのメッセージのやりとりは無視できません。 また、信頼性の高いのストリーム通信を必要としない場面では、オーバーヘッドを非常に小さくできます。

また、コネクションを確立せずに通信を行えますので、ブロードキャスト・アドレス宛にデータを送ることもできます。



キー入力した文字列を送る(一方向の通信)

UDPクライアント(送信側)

  1. 引数無しでDatagramSocketを作成する。
  2. 作成されたソケットのconnect()を実行する(省略可能)。 InetAddressとポート番号を引数に指定する。
  3. DatagramPacketオブジェクトを作成する。 これらのオブジェクトはbyte[]を包んだものである。 このbyte[] 中に送信データを入れたり、受信データが入ったりする。 受信をした場合は byte[] の大きさが、受信データの実際の大きさに変更されるので注意が必要である(大きくはならない)。
  4. connect()を実行していなければ、DatagramPacketのコンストラクタを 呼び出すとき、InetAddressとポート番号を指定する。
  5. パケットの長さを設定し、DatagramSocket#send(DatagramPacket)を使ってサーバにデータを送信する。
  6. DatagramSocket#receive()を使ってデータを受信する。

パケットの長さは、直前に処理したデータグラムの長さに応じて設定されるため、 元のバッファサイズよりも小さくなることがあります。 そのため、パケットを再利用する場合はバッファサイズをリセットする必要があることに注意が必要です。



UDPSendClient.javaの実行例
$ javac UDPSendServer.java 
$ java UDPSendClient 
abc
def
print
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbb <--本来は改行なし
bbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccccccccccccddddddddddddddddd <--本来は改行なし
dddddddddddddddddddddddddddddeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeefff <--本来は改行なし
fffffffffffffffffffffffffffffffggggggggggggggggggggggggggggggggggggggg <--本来は改行なし
ggggggggggggghhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhiiiiiiiiiiiiiiiiiiiiiiii <--本来は改行なし
iiiiijjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
^C <-- Controlキーを押しながらCのキーを押すとプログラムは強制終了される (SIGINTを送るので)
$ java UDPSendClient -h 172.17.1.255  <-- ブロードキャストアドレスに送ってみる
to broadcast

津田塾大学計算センターs205wsの教室のネットワークのネットワーク・アドレスは 172.17.0.0/23 なので、 ブロードキャスト・アドレスは 172.17.1.255 となります(2019年4月8日時点)。


UDPサーバ(受信側)




UDPRecvServer.javaの実行例
$ javac UDPRecvServer.java
$ java UDPRecvServer
/127.0.0.1:62821 abc
/127.0.0.1:62821 def
/127.0.0.1:62821 print
/127.0.0.1:62821 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbb <--本来はつながっている
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccccccccccccdddddddd <--本来はつながっている
ddddddddddddddddddddddddddddddddddddddeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee <--本来はつながっている
eeffffffffffffffffffffffffffffffffffgggggggggggggg <--バッファ長よりも長い部分は切れる
to boradcast  <--- ブロードキャストアドレス宛も受け取ることができる

[注意] UDPサーバが文字列を受け取るためには、UDPクライアントに文字列を入力する時点でUDPサーバが起動している必要があります。



UDPで双方向にデータをやりとりする

UDPクライアント (キー入力を送信し、返事を受信する)




UDPEchoClient.javaの実行例
$ javac UDPEchoClient.java
$ java UDPEchoClient -p 17938
abc
Received: abc
def
Received: def
end
Received: end

UDPサーバ (データを受信し、送り返す)

UDPEchoServer.java の変更点
*** udp/UDPRecvServer.java	2019-04-06 15:04:19.000000000 +0900
--- udp/UDPEchoServer.java	2019-04-06 15:04:40.000000000 +0900
***************
*** 3,11 ****
  import java.util.*;
  
! public class UDPRecvServer {
      UDPServer serv;
      int maxlen;
  
!     public UDPRecvServer(int port, int maxlen) {
  	serv = new UDPServer(port);
  	this.maxlen = maxlen;
--- 3,11 ----
  import java.util.*;
  
! public class UDPEchoServer {
      UDPServer serv;
      int maxlen;
  
!     public UDPEchoServer(int port, int maxlen) {
  	serv = new UDPServer(port);
  	this.maxlen = maxlen;
***************
*** 23,26 ****
--- 23,27 ----
  		String s = new String(data);
  		System.out.println(pkt.getAddress()+":"+pkt.getPort()+" "+s);
+ 		serv.send(pkt);
  		if (s.equals("end")) break;
  		pkt.setLength(maxlen); // resize data buffer
***************
*** 42,46 ****
  	    }
  	}
! 	UDPRecvServer prog = new UDPRecvServer(p, mlen);
  	prog.run();
      }
--- 43,47 ----
  	    }
  	}
! 	UDPEchoServer prog = new UDPEchoServer(p, mlen);
  	prog.run();
      }
UDPEchoServer.javaの実行例
$ javac UDPEchoServer.java
$ java UDPEchoServer -p 17938
/127.0.0.1:59580 abc
/127.0.0.1:59580 def
/127.0.0.1:59580 end