java によるネットワーク

  1. ネットワークの基礎知識
    1. TCP/IP
    2. Socket
  2. Java によるネットワーク
    1. ネットワークライブラリ
    2. TCP/IP クライアント・サーバモデル
      1. java によるメール送信
      2. Mail クラス
    3. Web の基礎
      1. URL
      2. URL1 クラス
      3. HTTP の例
      4. Http クラス
      5. クライアント・サーバモデルの例

  1. ネットワークの基礎知識
  2. ネットワーク上の各マシンをノードと呼びます。 ノードの多くはコンピュータですが、プリンタ、ルータなどもあります。 ネットワーク上のノードには、 必ずそのノードを識別するための固有のアドレスが設定されています。

    ネットワークによっては、ノードに対してわかりやすい名前をつけることがあります。 通常1つの名前が1つのアドレスに対応しますが、これは固定されてはいません。 ドメインネームシステム(DNS:Domain Name System)など。

    プロトコルとは、 コンピュータが通信する方法を定義するための規則です。 公開されているプロトコルを使えば、 異なるメーカのソフトウェアや装置を使っても相互に通信できます。

    IP(Internet Protocol), UDP(User Datagram Protocol), TCP(Transmission Control Protocol), FTP(File Transfer Protocol), SMTP(Send Mail Transfer Protocol), HTTP(HyperText Transfer Protocol) などが有名です。

    1. TCP/IP
    2. ネットワークの本質は、ビット列を A 地点から B 地点へ移動することです。 ふつうビット列に行き先を書いたビットをつけて、1つのパケットにします。 これが Internet Protocol すなわち IPです。

      このとき B 地点を示すものをアドレスといい、 4バイト(IPv4)で表現されます(16バイト(IPv6)になりつつあります)。 A 地点から B 地点への経路をルーティングといいます。

      1つのパケットに入りきらないビット列を送る場合は、 ビット列をグループ分けして、いくつかの連続したパケットとして送ります。 パケットのヘッダには、目的地となるマシンのアドレスと、 発信元のマシンのアドレスが含まれます。 途中で通過するルータは、あて先アドレスを調べて、最適なルートを選択します。

      パケットが送られた順番で確実に配送されることを保証するプロトコルがあります。 これは Transmission Control Protocol すなわち TCP です。

      TCP は、ルーティングやビット列を正しいアドレスに配送するのに IP を下位のプロトコルとして用います。 そのためこのプロトコルを TCP/IP といいます。 TCP ではそのセッションの間、実際に1対1の接続が開かれています。

      User Datagram は、User Datagram Protocol すなわち UDP を用いて インタネット越しに送信できます。 UDP はアドレスやルーティングを IP に頼っています。

      UDP では、いったん出してしまうと、到着する順序はわからないし、 到着しないでなくなってしまうかもしれません。 しかし、TCP は順序どうりに配送されることを保証しますが、 そのぶんオーバヘッドが大きくなります。

      UDP を郵便に例えれば、TCP は電話のようなものです。

      コンピュータは、同時に様々な処理を実行します。 従って1つのアドレス、 つまり1台のコンピュータに来た要求を区別する必要があります。 これを解決するのがポートです。

      1つのアドレスをもつコンピュータには、 それぞれ 65535論理ポートがあり、 ポートごとに特定のサービスを割り当てることができます。

      1から1023までのポート番号は、 一般によく知られたサービスのために予約されていますが、 この割り当ては絶対的なものではありません。 例えば、通常 FTP は21、SMTP は25、HTTP は 80などです。

      UNIX システムでは、/etc/services にポートの割り当て情報があります。

      TCP/IP のセッションの端点のアクセスオブジェクトは Socketです。 ソケットは、 1つの UNIX システム上のプロセス同士が通信するための方法です。

      ソケットの接続には、クライアント側とサーバ側があります。 一般的にクライアント側は、 接続を要求して情報をサーバに投げたり 要求したりします。 一方サーバ側は、 要求がくるのを待ち、要求に応じて様々な処理をします。

      一方、コネクションレスプロトコルである UDP をサポートするのが DatagramSocket です。

    3. Socket
    4. Socket は、2つのマシン間の通信の端点で、 双方向のストリーム型転送サービスを提供します。

      図:ソケットの通信手順

      図1:ソケットの通信手順

  3. Java によるネットワーク
    1. ネットワークライブラリ
    2. ネットワークライブラリは java.net にあり、次の主なクラスがあります。

      クラス 説          明
      InetAddress IP アドレスとその操作をおこなうクラス
      DatagramPacket データグラムパケットを表現するクラスで、 パケットのデータ、長さ、アドレス、ポートを含み、送受信可能です。
      DatagramSocket データグラムを UDP を使って送受信するクラス
      PlainSocketImpl ソケットのデフォルトの実装クラス
      SocketImpl ソケットの実装クラス。抽象クラスでこのサブクラスを作って、 実際に実装しなければならない。
      ServerSocket サーバのソケットのクラス。SocketImpl を使って実際のソケットを操作する。
      Socket クライアントのソケットのクラス。SocketImpl を使って実際のソケットを操作する。
      URL Web 上のオブジェクトの参照を表すクラス。 URL を知って内容を取得、変更できる。

      java Core API の java.net を見てみましょう。

      whoAreYou は、 コマンド行で指定した DNS 名から IP アドレスを求めるクラスです。

      import java.io.*;
      import java.net.*;
      
      class whoAreYou {
      
         public static void main(String[] args) throws IOException {
            if (args.length != 1)  {
               System.out.println("Usage : java whoAreYou HOST_NAME");
               System.exit(1);
            }
            InetAddress InetAddr = InetAddress.getByName(args[0]);
            System.out.println("Inet Address is " + InetAddr.toString());
         }
      }
      
      osaka-141] java whoAreYou nagano
      Inet Address is nagano/172.17.176.30
      

    3. TCP/IP クライアント・サーバモデル
    4. クライアント・サーバが、 TCP/IP のネットワーク接続を利用してどのように通信するか見てみましょう。

      プロセスどうしは、IP アドレスポート番号を知ることで接続できます。
      クライアントとサーバは、同じポート番号を使わなければなりません。

      ネットワークのソケット接続は2つのストリームのように見えます。 ソケットは双方向通信をサポートしていますので、 入力ストリームを取り出すメソッドと出力ストリームを取り出すメソッドがあります。 これによって、クライアントとサーバは相互に通信できます。

      インタネット上のプログラムの多くは、クライアントとサーバのペアで機能します。 サーバはどこかにあるホストシステムで、 クライアントはローカルなシステム上のプログラムです。

      要求を構成するビット列は、データグラムに組み立てられサーバに送られます。 サーバは入ってくるパケットを読み解釈し、サービスを提供するために、 インタネットを介して返答します。

      1. java によるメール送信
      2. はじめの例として、メールをあるホストマシンにおくるプログラムを見てみましょう。 ここでしようとすることは、システムの25番ポートでソケットをオープンし、 メールデーモンと、メールプロトコルでおしゃべりすることです。

        SMTP メールプロトコルは以下のとおりです。

        mail from:  送り主 
        recp to:    あて先
        data        本文
          本文
        .           本文の終わり
        
        

      3. Mail クラス
      4. import java.io.*;
        import java.net.*;
        
        public class Mail {
           static final int MAILPORT = 25;
           public static void main(String[] args) throws IOException {
              String addr_to = "taka";
              String addr_fr = "taka";
              String data    = "data";
              String mail_fr = "mail from : " + addr_fr;
              String recp_to = "rcpt to: "    + addr_to ;
              String msg     = "This is the message\nthat java sent this.";
              String eof     = ".";
        
              Socket          sock;
              BufferedReader  dis;
              BufferedWriter  ps;
              String[]  send ={mail_fr,recp_to,data,msg,eof};
        
              sock = new Socket("localhost",MAILPORT);
              dis  = new BufferedReader(new InputStreamReader(sock.getInputStream()));
              ps   = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream()));
        
              for (int i=0; i< send.length; i++) {
                 ps.write(send[i],0,send[i].length());
                 ps.newLine();
                 ps.flush();
                 System.out.println(dis.readLine());
              }
        
              ps.close();
              dis.close();
              sock.close();
           }
        }
        

    5. Web の基礎
    6. HTTP(HyperText Transfer Protocol) は、 Web クライアントとサーバの対話、 またサーバからクライアントにデータを転送する方法を定義する標準です。 HTTP は、MIME(Multipurpose Internet Mail Extension)HTML(HyperText Markup Language) という2つの標準に依存しています。

      MIME は、音声やテキストなど異なる種類のデータを ASCII 接続を介して転送するためのエンコード形式です。

      HTML は、テキスト形式の Web ページを記述する言語で、 URL(Uniform Resource Locators) で識別される別のドキュメントへのリンクを指定できます。 このテキストも HTML で書かれています。

      1. URL
      2. URL は、インターネット上のファイルの位置を指定する形式で、 構成は以下のとおりです。

          プロトコル名:ホスト名[:ポート]/パス/ファイル名[#セクション]
        

      3. URL1 クラス
      4. import java.net.*;
        
        class URL1 {
           public static void main(String[] args) {
              URL  webURL,ftpURL;
        
              try {
                 webURL = new URL("http://perugia:80/java/index.html");
                 System.out.println(webURL);
                 System.out.println("");
                 System.out.println("Protocol  : " + webURL.getProtocol());
                 System.out.println("Host      : " + webURL.getHost());
                 System.out.println("Port      : " + webURL.getPort());
                 System.out.println("File      : " + webURL.getFile());
                 System.out.println("Ref       : " + webURL.getRef());
                 System.out.println("");
                 ftpURL = new URL("ftp://perugia/java/");
                 System.out.println(ftpURL);
              }  catch (Exception e) {
                 System.err.println(e);
              }
           }
        }
        
        osaka-26] java URL1
        http://perugia:80/java/index.html
        
        Protocol  : http
        Host      : perugia
        Port      : 80
        File      : /java/index.html
        Ref       : null
        
        ftp://perugia/java/
        

      5. HTTP の例
      6. HTTP は、Web ブラウザ(クライアント)と Web サーバ(サーバ)がデータをやりとりするための標準プロトコルで、 HTTP 接続では TCP/IP を使いデータを転送します。

        基本プロトコルは、クライアントからサーバへのデータ要求を、 次の4段階で定義します。

        1. 適当なポート番号でサーバに接続します。
        2. 指定した URL のページを要求するメッセージをサーバに送ります。
          メッセージの形式は以下のとおりです。

          GET URL HTTP/1.0
          Accept: text/html
          Accept: text/plain
          Accept: image/gif
          Accept: image/jpeg
             :
          空行
          

        3. サーバからの応答を受信します。
        4. 接続を切断します。

      7. Http クラス
      8. import java.io.*;
        import java.net.*;
        
        public class Http {
           static final int HTTP_PORT = 80;
           public static void main(String[] args) throws IOException {
              String[]         cmd       = {"GET /java/index.html HTTP/1.0",
                                            "Accept: text/html",
                                            "Accept: text/plain"};
              String           webserver = "perugia";
              String           s         = null;
              Socket           sock;
              BufferedReader   dis;
              BufferedWriter   ps;
        
              sock = new Socket(webserver,HTTP_PORT);
              dis  = new BufferedReader(new InputStreamReader(sock.getInputStream()));
              ps   = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream()));
        
              for (int i=0; i< cmd.length; i++)  {
                 ps.write(cmd[i],0,cmd[i].length());
                 ps.newLine();
              }
              ps.newLine();
              ps.flush();
        
              while ((s=dis.readLine()) != null)
                 System.out.println(s);
              sock.close();
           }
        }
        

        注意 当然のことですが、これらのプログラムは IP アドレスを持っていて TCP/IP のネットワークに接続しているコンピュータ上でなければ動きません。

        UNIX システムでは TCP/IP のサポートは OS の基本的な機能です。

        Mail.java URL1.java Http.java をつくり実行してみましょう。
        ただし、Mail.java では java_std にメールを送りましょう。

      9. クライアント・サーバモデルの例
      10. サーバとクライアントの2つのプログラムから構成されます。

        HttpS は、クライアントからのファイル転送要求に対して、 HttpS を起動したときに指定したファイルを常に送信する HTTP サーバです。 一方 HttpC は、HTTP サーバに接続し、 送信されるファイルを受け取るクライアントです。 HttpS を起動後、HttpC を実行すると、そのファイルを受け取れます。

        以下は、HttpS のソースと telnet を使ってこのサーバに接続したときの実行結果です。

        HttpS サーバ

        /* 
            HttpS -- HTTP server
        */
        import java.net.*;
        import java.io.*;
        import java.util.*;
        
        class HttpS extends Thread {
           static final int MIN_ARGS = 1;
           static final int MAX_ARGS = 2;
           static final int MAX_PORT = 0XFFFF, DEF_PORT = 4444;
           static final String HTML = ".html";
           static final String HTM  = ".htm";
           static final String END_OF_LINE = "\n";
           static final String HTTP = "HTTP/";
           static final String HTTP_OK     = "HTTP/1.0 200 OK ";
           static final String PLAIN_TEXT  = "text/plain";
           static final String HTML_TEXT   = "text/html";
           static final String DATE        = "Date : ";
           static final String SERVER      = "Server : HttpS 1.0";
           static final String CONTENT_LENGTH  = "Content-length : ";
           static final String CONTENT_TYPE    = "Content-type : ";
           static StringBuffer  theData;
           static String        Data;
           static String ContentType;
           static int    ContentLength;
           Socket        theConnection;
           String[] cmd ={HTTP_OK,
                          DATE,
                          SERVER,
                          CONTENT_LENGTH,
                          CONTENT_TYPE };
        
           HttpS(Socket s) {
              theConnection = s;
           }
        
           public static void main(String[] args) {
             int port;
             ServerSocket  ss;
             Socket        conn;
             InputStream  file;
        
        // check input arguments
             if ((args.length < MIN_ARGS) || (args.length > MAX_ARGS))   usage();
        // open the file
             try {
                file = new FileInputStream(args[0]);
                BufferedReader  dis = new BufferedReader(
                                        new InputStreamReader(file));
                if (args[0].endsWith(HTML) || args[0].endsWith(HTM)) 
                   ContentType = HTML_TEXT;
                else
                   ContentType = PLAIN_TEXT;
                String thisLine;
                theData = new StringBuffer();
        
        // get file content and cache it
                while ((thisLine = dis.readLine()) != null) {
                   theData = theData.append(thisLine).append(END_OF_LINE);
                }
                Data = theData.toString();
                ContentLength = Data.length();
        //  get port number
                if (args.length == NO_ARGS)  port = DEF_PORT;
                else {
                   port = Integer.parseInt(args[1]);
                   if ((port <0) || (port >MAX_PORT))  port = DEF_PORT;
                }
        //  open server socket
                ss = new ServerSocket(port);
                System.out.println("Accepting connections on port " + ss.getLocalPort());
                System.out.println("Data to be sent : ");
                System.out.println(Data);
                while (true) {
                  HttpS fs = new HttpS(ss.accept());
                  fs.start();
                }
             }  catch (Exception e) {
                System.out.println(e);
             } 
           }
        
           public void run() {
             try {
                BufferedWriter os = new BufferedWriter(new OutputStreamWriter(
                                            theConnection.getOutputStream()));
                BufferedReader is = new BufferedReader(new InputStreamReader(
                                            theConnection.getInputStream()));
        // check command
                String request = is.readLine();
                if (request.indexOf(HTTP) != -1) {
                   for(int i=0;;i++) {
                      String thisLine = is.readLine();
                      if (thisLine.trim().equals(""))  break;
                      System.out.println(thisLine);
                   }
        // send MIME header
                   Date now = new Date();
                   cmd[1] = new String(DATE + now);
                   cmd[3] = new String(CONTENT_LENGTH + ContentLength) ;
                   cmd[4] = new String(CONTENT_TYPE   + ContentType);
        
                   for(int i=0;  i< cmd.length; i++)  {
                      os.write(cmd[i],0,cmd[i].length());
                      os.newLine();
                      System.out.println(cmd[i]);
                   }
                }
        // send file contents
                os.write(Data,0,Data.length());
                os.newLine();
                os.flush();
                theConnection.close();
             }  catch (Exception e) {}
           }
        
           static void usage() {
              System.out.println("Usage : java HttpS FILE_NAME [PORT_NUMBER:4444]");
              System.exit(NO_ARGS);
           }
        }
        
        osaka-42] telnet osaka 4444
        Trying 172.17.176.40...
        Connected to osaka.
        Escape character is '^]'.
        GET / HTTP/1.0
        
        HTTP/1.0 200 OK
        Date : Thu Oct 12 11:07:38 JST 2000
        Server : HttpS 1.0
        Content-length : 296
        Content-type : text/html
        <html LANG="ja">
        <head>
        <title> Java 講座</title>
        </head>
        
        <frameset cols="22%,*">
         <frame src="java_idx.html" marginwidth=1 marginheight=10 name="index" target="index">
         <frame src="intro.html"     marginwidth=20 marginheight=30 name="main" target="main">
        <noframe>
        </noframe>
        </frameset>
        </html>
        
        Connection closed by foreign host.
        

        HttpC クライアント

        /*
             HttpC -- HTTP client
        */
        import java.io.*;
        import java.net.*;
        
        public class HttpC {
           static final int NO_ARGS   = 1;
           static final int MAX_PORT = 0XFFFF,DEF_PORT = 4444;
        
           public static void main(String[] args) throws IOException {
              String[]         cmd       = {"GET / HTTP/1.0",
                                            "Accept: text/html",
                                            "Accept: text/plain"};
              String           webserver = "osaka";
              String           s         = null;
              Socket           sock;
              BufferedReader   dis;
              BufferedWriter   ps;
              int              port = DEF_PORT;
        
              if (args.length == NO_ARGS) {
                 port = Integer.parseInt(args[0]);
                 if ((port <0) || (port >MAX_PORT))  port = DEF_PORT;
              }
              sock = new Socket(webserver,port);
              dis  = new BufferedReader(new InputStreamReader(sock.getInputStream()));
              ps   = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream()));
        
              for (int i=0; i< cmd.length; i++)  {
                 ps.write(cmd[i],0,cmd[i].length());
                 ps.newLine();
              }
              ps.newLine();
              ps.flush();
        
              while ((s=dis.readLine()) != null)
                 System.out.println(s);
              sock.close();
           }
        }