■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
                      2007年09月09日

    楽しいJava講座 - 初心者から達人へのパスポート
                  vol.069

                                セルゲイ・ランダウ
 バックナンバー: http://www.flsi.co.jp/Java_text/
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■


[このメールマガジンは、画面を最大化して見てください。]


========================================================
◆ 01.ネットワーク系のプログラミング
========================================================


UDPによるソケット通信の手順を簡単に説明しましょう。

まず、クライアントは通信の窓口を開くためにソケットのオブジェ
クト(インスタンス)を生成しますが、このときTCPとは異なり、
相手(サーバー)のIPアドレスとポート番号を指定する必要はありま
せん。
UDPでは、サーバーへの接続は行わず、相手につながっているかどうか
を確認することなく、いきなりデータを送りつけるので、データの
送信時に指定すれば済むからです。

このソケットのインスタンス生成は、例えば
--------------------------------------------------------
DatagramSocket socket = null;
socket = new DatagramSocket();
--------------------------------------------------------
という形式で、行います。
上記を見るとわかるように、UDPの場合のソケットのクラス名は
DatagramSocketです。
(DatagramSocketはjava.netパッケージにはいっています。)
なお、なんらかの理由でDatagramSocketのインスタンス生成ができ
なかった場合にはSocketExceptionが発生します。

UDPによるソケット通信では、データはデータグラム・パケット
(Datagram Packet)と呼ばれる形式で送信されます。

パケット(Packet)の元々の意味は「小包」ですが、ネットワーク
における用語としては、一つの送信作業で送られるデータの単位
(一まとめのデータ)を意味します。いくつかの品物を小包に一ま
とめにして送るイメージから、いくつかのデータを一まとめにして
(それをパケットと呼び)送信するわけです。
データグラム(Datagram)というのは、(重さの)単位を表すグラム
(gram)とデータを組み合わせて作られた造語で、データの単位を
意味します。つまり、パケットと同じ意味です。
(なお、gramにはまたギリシャ語からの借用で(データが)「書かれた
もの」という意味もある。)
ただし、パケットが汎用的に使われる言葉なのに対し、データグラム
はUDPに限定して使われる言葉です。

データグラム・パケットには、データのほかに宛先も書かれます。
つまり送信先のIPアドレス、ポート番号も書かれます。

データグラム・パケットは以下のようにして作成します。
--------------------------------------------------------
DatagramPacket packet = null;
packet = new DatagramPacket(データ, データの長さ, IPアドレス, ポート番号);
--------------------------------------------------------
つまり、送信するデータ, データの長さ, 送信先のIPアドレス, 送信先
のポート番号を指定してDatagramPacketクラスのインスタンスを生成
します。(DatagramPacketはjava.netパッケージにはいっています。)
この引数の型は以下の通りです。
--------------------------------------------------------
データ : byte[]
データの長さ : int
IPアドレス : InetAddress
ポート番号 : int
--------------------------------------------------------
ここで、InetAddressというのは、IPアドレスを表現するクラスで、
java.netパッケージにはいっています。


┌補足─────────────────────────┐
IPアドレスの形式には、現在主として使われているIPv4(IPバージョン4)
の形式と、今後普及してくるIPv6(IPバージョン6)の形式の2種類があり
ます。
(ちなみにIPv5というものもIPv6の前に一時提案されましたが、現在は
消滅しています。)

IPv4では、IPアドレスは4バイト(32ビット)で表現され、1バイトずつ
10進数で書き、各バイトの間にピリオド(.)を入れて表記する習慣になっ
ています。
たとえば、
--------------------------------------------------------
123.45.67.212
--------------------------------------------------------
というように表記します。

なお、IPアドレスには、いくつかの特殊な目的で使用される番号があり
ます。
たとえば、
192.168.0.0から192.168.255.255までのIPアドレスは、プライベイト
(個人的)なネットワーク(private network)用の番号であり、家庭や
会社のLAN環境などの私的なネットワーク内でのみ個人が自由に
使える番号になっています。
詳しくは、
http://www.ietf.org/rfc/rfc3330.txt
などを参照してください。

一方、IPv6では、IPアドレスは128ビットで表現され、16ビット(2バイト)
ずつ16進数で書き、各16ビットの間にコロン(:)を入れて表記する習慣に
なっています。
たとえば、
--------------------------------------------------------
ABCD:EF12:3456:7890:12AB:34CD:56EF:AB78
--------------------------------------------------------
というように表記します。

IPv4形式のIPアドレスではアドレスが不足しつつあるために、IPv6形式の
IPアドレスが作られたのであり、今後はIPv6形式が段々と普及してきて、
最終的には全てのIPアドレスがIPv6形式に代わることになります。

しかし、当分の間は、主にIPv4形式のIPアドレスにお世話になることに
なるでしょう。

というわけで、当メールマガジンでも特に断らない限り、IPv4形式のIPア
ドレスを使用します。
└───────────────────────────┘

InetAddressにはInet4AddressとInet6Addressという2つのサブクラスがあり、
それぞれIPv4形式のIPアドレスとIPv6形式のIPアドレスを表現しています。


なお、IPアドレスは数字の羅列で覚えにくいので、通常はIPアドレスの代わり
にホスト名(IPアドレスに対応付けられた(ホストの)名前)を使用すること
が多いです。
ホスト名は、簡単なLANの環境であればhostsというファイル(たとえば
Windows XPであれば、C:\WINDOWS\system32\drivers\etcにhostsファイルが
入っています。hostsファイルは普通のテキスト・ファイルです)に登録され
ることによって、あるいは本格的なネットワークやインターネットであれば
DNS(Domain Name System)という、ドメイン名(Domain Name)を管理する
コンピューターに登録されていることによって、利用することができます。
(ドメイン名はホスト名と同じものと思って差し支えありませんが、厳密に
はドメイン名はホスト名とは若干意味が異なり、IPアドレスに対応していな
いことがあります。)

そして、ホスト名からIPアドレスを知りたいときには、

InetAddress ipAddress = InetAddress.getByName("ホスト名");

というふうにInetAddressのgetByName()メソッド(staticメソッド)を使う
ことができます。(なお、このとき、もし該当するホストが見つからない
場合にはUnknownHostExceptionが発生します。)

たとえば、localhostのIPアドレスを知りたいときは

InetAddress ipAddress = InetAddress.getByName("localhost");

とします。
あるいは、sun.comの
IPアドレスを知りたいときは

InetAddress ipAddress = InetAddress.getByName("sun.com");

とします。
なお、InetAddressのオブジェクトのままでは数値のIPアドレスはわかり
ませんので、toString()メソッドを使って、文字列を受け取るか、あるい
はgetAddress()メソッドを使って、数値(byteの配列)を受け取ります。
たとえば、

byte[] address = ipAddress.getAddress();

としてから、

System.out.println("IP address = " + address[0] + '.' + address[1] + '.' + address[2] + '.' + address[3]);

とすれば数値のIPアドレスをコンソールに表示させられます。


ちょっとわき道にそれましたが、たとえば、5, 4, 3, 2, 1というデータ
(byte型の数値の配列)を同じパソコン内の49152というポート番号の
プログラムに送り込むには、
--------------------------------------------------------
byte[] outData = {5, 4, 3, 2, 1};
int outLength = outData.length;
DatagramPacket packet = null;
packet = new DatagramPacket(outData, outLength, InetAddress.getByName("localhost"), 49152);
--------------------------------------------------------
というふうにしてデータグラム・パケットを作ればいいことがわかりますね。

そして、このパケットを送信するにはDatagramSocketのsend()メソッドを
使って、
--------------------------------------------------------
socket.send(packet);
--------------------------------------------------------
のようにします。

そして、送信が終わったら、ソケットをクローズします。
--------------------------------------------------------
socket.close();
--------------------------------------------------------

では、クライアントのソース・コードを確認してみてください。


[UDPを使ったクライアント]
--------------------------------------------------------
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;

public class UdpSocketClient {

   public static void main(String[] args) {
      DatagramSocket socket = null;
      byte[] outData = {5, 4, 3, 2, 1};
      int outLength = outData.length;
      DatagramPacket packet = null;

      try {
         socket = new DatagramSocket();
      } catch (SocketException e) {
         e.printStackTrace();
      }
      try {
         packet = new DatagramPacket(outData, outLength, InetAddress.getByName("localhost"), 49152);
         System.out.println("Data 54321 sent.");
      } catch (UnknownHostException e1) {
         e1.printStackTrace();
      }
      try {
         socket.send(packet);
      } catch (IOException e) {
         e.printStackTrace();
      }
      socket.close();
   }

}
--------------------------------------------------------

問題なく理解できますね。


続いてサーバーのほうを説明しましょう。
サーバーでは、ソケットのインスタンス生成時にポート番号を指定して
おきます。
例えば
--------------------------------------------------------
DatagramSocket socket = null;
socket = new DatagramSocket(49152);
--------------------------------------------------------
というふうにします。

続いてデータグラム・パケットのインスタンスを生成しますが、受信側
なのでIPアドレス、ポート番号は指定しません。
代わりに次のように行います。
--------------------------------------------------------
DatagramPacket packet = null;
packet = new DatagramPacket(データ, データの長さ);
--------------------------------------------------------

たとえば、
--------------------------------------------------------
byte[] inData = new byte[5];
int inLength = inData.length;
DatagramPacket packet = null;
packet = new DatagramPacket(inData, inLength);
--------------------------------------------------------
このとき、データを入れる配列の要素数(データの長さ)は送信側に合わ
せるようにしてください。もし、データの長さが不足していると、データ
が入りきらなかった分は単純に消失するだけであり、エラーも何も出ませ
ん。UDPは信頼性が保証されないプロトコルなのです。

次にデータの受信ですが、
--------------------------------------------------------
socket.receive(packet);
--------------------------------------------------------
というふうにDatagramSocketのreceive()メソッドを使います。
サーバーは通常、この処理をループで繰り返すことになります。

サーバーのプログラムを終了するときには、
--------------------------------------------------------
socket.close();
--------------------------------------------------------
というふうにソケットをクローズしておきます。

では、サーバーのソース・コードを確認してみてください。


[UDPを使ったサーバー]
--------------------------------------------------------
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class UdpSocketServer {

   public static void main(String[] args) {
      DatagramSocket socket = null;
      byte[] inData = new byte[5];
      int inLength = inData.length;
      DatagramPacket packet = null;

      try {
         socket = new DatagramSocket(49152);
      } catch (SocketException e) {
         e.printStackTrace();
      }
      packet = new DatagramPacket(inData, inLength);
      for(int n = 0; n < 5; n++) {
         try {
            socket.receive(packet);
         } catch (IOException e) {
            e.printStackTrace();
         }
         for (int i = 0; i < inLength; i++) {
            System.out.print(inData[i]);
         }
         System.out.println();
      }
      socket.close();
   }

}
--------------------------------------------------------

これも容易に理解できますね。


では、次回はいよいよTomcatを使うべくツール類を整えていきます。

何か、わからないところがありましたら、下記のWebページまで質問を
お寄せください。


(続く)




========================================================
◆ 02.Java(文法等)解説 [JavaBeans (4)]
========================================================

[JavaBeans (4) イベント<1>]

ActionEventなどの既存のイベントについては既にお話しましたが、
自分で独自のイベントを作ることもできます。

独自のイベントは以下の作業によって定義されます。
(仮にMyOwnEventという名前のイベントにします。)

(1) イベント・オブジェクトの定義
EventObjectクラスを継承したMyOwnEventクラスを定義します。

(2) イベント・リスナーのインターフェースの定義
EventListenerインターフェースを拡張してMyOwnEventListenerと
いうインターフェースを作り、イベント通知時に呼び出されるメ
ソッド(リスナー・メソッド)を定義します。


(続く)



以上、今回は
┌───────────────────────────┐
・UDPによるソケット通信のプログラミング
・InetAddressクラスの使いかた
└───────────────────────────┘
を学習しました。
では、また来週。



================================================
◆ 03.演習問題
================================================

1.
上記のInetAddressの使い方を理解するために次のようなプログラム
を作って実行してみてください。

--------------------------------------------------------
import java.net.InetAddress;
import java.net.UnknownHostException;

public class InetAddressTest {

   public static void main(String[] args) {
      InetAddress ipAddress;
      byte[] address;
      try {
         ipAddress = InetAddress.getByName("localhost");
         System.out.println(ipAddress.toString());
         address = ipAddress.getAddress();
         System.out.println("IP address = " + address[0] + '.' + address[1] + '.' + address[2] + '.' + address[3]);
      } catch (UnknownHostException e) {
         e.printStackTrace();
      }
      try {
         ipAddress = InetAddress.getByName("sun.com");
         System.out.println(ipAddress.toString());
         address = ipAddress.getAddress();
         System.out.println("IP address = " + address[0] + '.' + address[1] + '.' + address[2] + '.' + address[3]);
      } catch (UnknownHostException e) {
         e.printStackTrace();
      }

   }

}
--------------------------------------------------------


2.
UdpSocketServerのソース・コードの中の
--------------------------------------------------------
byte[] inData = new byte[5];
--------------------------------------------------------
の部分を
--------------------------------------------------------
byte[] inData = new byte[2];
--------------------------------------------------------
に書き換え(つまり送信側より配列の要素数を少なくして)、ソケット
通信のテストをしてみてください。
エラー(Exception)は出ないけれど、データが部分的に消失すること
を確認してください。



┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
★ホームページ:
      http://www.flsi.co.jp/Java_text/
★このメールマガジンは
     「まぐまぐ(http://www.mag2.com)」
 を利用して発行しています。
★バックナンバーは
      http://www.flsi.co.jp/Java_text/
 にあります。
★このメールマガジンの登録/解除は下記Webページでできます。
      http://www.mag2.com/m/0000193915.html
★このメールマガジンへの質問は下記Webページにて受け付けて
 います。わからない所がありましたら、どしどしと質問をお寄
 せください。
      http://www.flsi.co.jp/Java_text/
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

Copyright (C) 2007 Future Lifestyle Inc. 不許無断複製