時刻サーバー

  1. サーバー&クライアント方式

    1. サーバー&クライアント方式

      サーバーはクライアントからの接続を待ち、接続に応じて情報提供などのサービスをします。サーバーは情報提供だけでなくネットワークの中継局の役目を果たします。クライアントはサーバーに接続することで、サーバーに情報を提供したり、他のクライアントからのデータを受け取ることができます。ここではネットワーク接続に TCP/IP を利用します。


    2. TCP/IP

      TCP/IPでは、IPによりネットワーク上のPC(正確にはNIC:ネットワークカード)を識別します。IPは1バイト4桁の数字とマスクで指定します。自分のPCの IP はコマンドプロンプトで知ることが出来ます。
           >ipconfig /all
       IPにはインターネットで利用できるグローバルIPとLAN内部(限定された範囲)でのみ利用できるローカルIPがあります。ローカルな IP には 192.168.0.*  (*は任意、マスク 255.255.255.0)などがよく利用されます。また、127.0.0.1 は自分のIPとなります。
       接続法には UDP/IP もあります。UDP/IP は事前の接続手続きは不要で、いきなりデータを送ることが出来ます。効率的な方法ですが、送信データの検証機能がないため「安全性」は低くなります。
       IP は手動で設定できますが、ネット上にDHCP サーバーが存在する場合、DHCP からIPを自動的に取得できます。DHCPサーバーが存在しない場合、マニュアルでIPを設定する必要があります

    3. ポート

       TCP/IPでは、指定したIP番号をもつPC(パソコン)と接続し、PORT(ポート) 番号で処理するプログラムと交信します。PORT番号は、メール、web、FTP、などのプロトコルに対応します。クライアントはサーバーのIPとポート番号を指定してサーバーにパケットを送ります。サーバーはOSから自分のポート番号に対する受信データ(パケット)を受け取ります。
       クライアントのポート番号は任意で、クライアント側で未使用のポート番号を利用します(多くは自動生成される番号を利用します)。パケットには宛先と送り主のIPとポート番号が含まれます。サーバーは受信したパケットから接続したクライアントのポート番号を知ることができます。
      プロトコル 内容 ポート番号
      FTP ファイル送受信 22,23
      DHCP IP自動割付 67,68
      HTTP web 80
      smtp メール送信 25
      pop3 メール取得 110
      NTP 時刻の取得 123

    4. ルータ

       データはパケットと呼ばれる単位で、分割して送られます。パケットを流す経路を定める装置をルータといいます。ルータには、ローカルな IP をグローバルな IP に変換して送受信する機能(IPマスカレード)、指定以外のポート番号を通過させないポートフィルタ機能、新しく接続したPCの要求に応じてIPを渡す DHCPサーバー機能、などが実装されています。


    5. コネクションとソケット

       TCP/IPでは接続する相手に予め接続依頼を出し、接続許可を得る必要があります。サーバーは予めサーバーソケットを作成しておく必要があります。接続依頼はクライアント側で送受信用のソケットを作成するときに行われます。サーバーから接続許可を受けるとソケットが作成され、このソケットを通して送受信が可能になります。

    6. ストリーム

       受信データはデータ受信用ソケットで受け取り、これをストリームに流し込みます。文字型のストリームは、入力の場合 InputStreamReader を経て、文字単位で文字を受け取ります。出力の場合、OutputStream を経て、ソケットにデータを送ります。
       ストリーム入出力はファイルとも直結しており、ネットワークからの送受信もファイルからの入出力も同じように扱うことができます。

  2. 時刻サーバー

    1. 時刻サーバー

      クライアントからの接続を待ち、日付と時刻を送信します。PCでは日付と時刻は次のように取得できます。このサーバーは正式なNTPサーバーではなく、近距離での時刻あわせに利用できます。
      Date d ;
      d = new Date() ;
      d.toString() で日付と時刻の文字列を生成できます。WindowXP以後では、NTPサーバーによる時刻更新が自動的に行われます。

    2. ソケット

       サーバーはポート番号(ここでは6000)を指定してサーバー用のソケットを作成します。
         servsock = new ServerSocket(6000) ;
      次に、sock = servsock.accept() でクライアントからの接続を待ちます。接続がくるまで、このプログラムは「お休み状態」になります。接続があると、送受信用のデータソケット sock を受け取り、 out = sock.getOutputStream() ;  で送信用のストリーム(ファイルの一種)を作成し、out.write( ) ; で文字を送信します。

    3. 文字列処理

      Stering は文字列を記録するクラスで、 + 演算は文字列を接続します。length() 関数は文字列の長さを返します。
      String outstr  ;
       outstr = "\n" + " this is time server." + "\n" + d.toString() + "\n";
       int ln = outstr.length()

    4. ソース


      //timeServer.java
      // 接続があるとPC内部の時計の時刻を知らせるサーバプログラム
      // このプログラムはポート番号6000番で動作するTCPサーバです.
      // このプログラムを停止させるにはコントロールCを入力して下さい.
      //実行 java timeServer
      
      // ライブラリの利用
      import java .io.*  ;
      import java.net.*  ;
      import java.util.* ;
      
      // クラスtimeServer
      class timeServer{
      
          public static void main(String args[]){
      
          ServerSocket servsock = null ;// サーバ用ソケット
          Socket sock ;// ソケットの読み書き用オブジェクト
      
          OutputStream out ;// 出力ストリーム
          String outstr  ;// 出力データを格納する文字列
      
          int i ;
          Date d ;// 日付時刻用オブジェクト
          System.out.println(" control C で終了");
      
          try{
            // サーバソケットの作成
            servsock = new ServerSocket(6000) ;
            System.out.println(" サーバー socket 作成");
      
            // サーバ処理の繰り返し
            while(true){
               System.out.println("接続を待っています");
               sock = servsock.accept() ;//接続処理
               System.out.println("接続がありました");
      
               // 出力用時刻データの作成
               d = new Date() ;
               outstr = "\n" + " this is time server."
                   + "\n" + d.toString() + "\n";
      
              // データの出力
              out = sock.getOutputStream() ;
              for(i = 0; i < outstr.length();++i)
                    out.write((int)outstr.charAt(i)) ;
               out.write('\n') ;
               System.out.println("送信終了\n");
      
               // 接続終了
               sock.close() ;
               
            }
          }catch(IOException e){//エラー処理
            System.exit(1) ;
          }
        }
      }

  3. タイムクライアント

    1. タイムクライアント

      サーバーに接続し、時刻を取得します。起動するとき、コマンド行でサーバーのIPとポート番号を指定します。
           例 java timeClient サーバーIP番号 6000
       サーバーのIP番号とポート番号は、main(String[] args) の引数となり、args[0]にサーバーIP、args[1]にポート番号が渡ります。
       まず、受信用のソケット readsocket を作成し、そこから受信用入力ストリームを作成し、そこからデータを読み取ります。このとき、クライアント側のポート番号は、自動生成されます。サーバー側では、接続パケットを見て、クライアント側のIPとポート番号を知ることができます。
        readsocket = new Socket(args[0],Integer.parseInt(args[1])) ;//ソケット作成
        instr =  readsocket.getInputStream() ;//受信ストリーム作成
        int n = instr.read(buff);//受信
       一定時間受信がないと、例外処理をうけ受信を終了します。最後にストリームとソケットと閉じます。

    2. ソース

       まず、ソケットを作成します。
           readsocket = new Socket(args[0],Integer.parseInt(args[1])) ;
      このとき、args[0] でコマンドから設定される IP 番号と、args[1] でポート番号を指定します。ソケットを作成すると、サーバーに対し接続要求が出ます。サーバーはこの要求を accept() で受け取ります。
       接続が成功すると、サーバーは 時刻メッセージを送ってきます。クライアントは、 readsocket.getInputStream() ; でメッセージを受け取り表示します。
      //timeClient.java
      // タイムサーバからデータを受け取り,時刻を画面に出力します.
      // 実行 java timeClient サーバーIP番号 6000
      //先にtimeServerを実行しておきます。
      
      //ライブラリの利用
      import java.io.*;
      import java.net.* ;
      
      //timeクライアント
      public class timeClient {
      
      // プログラムの本体 main
      
        public static void main(String[] args){
      
            byte[] buff = new byte[1024];//配列の定義
            Socket readsocket = null ;// サーバ接続用ソケット
            InputStream instr = null;// 読み取り用ストリーム
      
            boolean cont = true ;
            // 指定のポートに対応するソケットを作成し、接続要求を出す
            // args[0]はサーバーIP、args[1]はポート番号
            try{
              readsocket
                = new Socket(args[0],Integer.parseInt(args[1])) ;
              System.out.println(" クライアント socket 作成");
              instr =  readsocket.getInputStream() ;
            }
            catch(Exception e){
              System.err.println("接続できません") ;
              System.exit(1) ;
            }
            System.out.println("接続成功");
      
            while (cont) {
              try {
                  // 読み込み
                  int n = instr.read(buff);
                  // System.outへの書き出し
                  System.out.write(buff, 0, n) ;
                }
              // 以下は例外処理です、受信なく一定時間が経過すると例外処理が起こります、
              catch(Exception e){
                 cont = false ;//受信終了
              }
            }
      
            // コネクションを閉じます
            try{
              instr.close() ;
            }
            catch(Exception e){
              System.err.println("終了エラーです") ;
              System.exit(1) ;
            }
          }
      }

  4. 利用法

    1. IP番号

      自分のPCでサーバーとクライアントを作成し確認します。まず、ipconfig コマンドで自分のIPを確認します。
      E:\java\prog3A>ipconfig
      Windows 2000 IP Configuration
      Ethernet adapter ローカル エリア接続 3:
              Connection-specific DNS Suffix  . : aitai.ne.jp
              IP Address. . . . . . . . . . . . : 192.168.11.10
              Subnet Mask . . . . . . . . . . . : 255.255.255.0
              Default Gateway . . . . . . . . . : 192.168.11.1

    2. サーバーを起動

       サーバープログラムをコンパイルし、起動します。
      E:\java\prog3A>javac timeServer.java
      
      E:\java\prog3A>java timeServer
       control C で終了
       サーバー socket 作成
      接続を待っています
       サーバーを終了するには control C を押します。

    3. クライアント起動

       別のコマンドプロンプトを開きます。クライアントプログラムをコンパイルし起動します。サーバーのIPとポート番号(6000)を指定します。
      E:\java\prog3A>java timeClient 192.168.11.10 6000
       クライアント socket 作成
      接続成功
       this is time server.
      Wed Jun 20 08:47:05 JST 2007
       このとき、サーバー側にもメッセージが表示されます。
      E:\java\prog3A>java timeServer
       control C で終了
       サーバー socket 作成
      接続を待っています
      接続がありました
      送信終了

    4. 他のサーバーに接続

       クライアント起動時のサーバーのIPを変更して、隣のサーバーに接続してください。また、サーバーの区別がつくように、サーバーのメッセージを適当に修正してください。
       なお、ルータで許可する設定がない場合、ルータを越えて接続を行うことは出来ません。

    5. 制限事項

       このタイムサーバは正確な時間を刻んでいる保証はありません。接続したクライアントはサーバーの時刻にあわせることができます。サーバーとクライアントが離れていたり、ネットワークが混雑していると、伝播遅延が加わります。

    6. pingコマンド

      ping コマンドを利用すると、指定したIPやURLを持つサーバーのネットワーク接続をチェックすることができます。
       ping 192.168.11.20
        ping www.yahoo.co.jp

  5. その他


    1. 発展

      サーバーから、時報以外に適当なメッセージを送りなさい。
       例:14:20までにこの課題を完成させること

    2. NTPプロトコル

       インターネット上では、ネットワークから時刻を取得する専用のプロトコルがあります。この場合、サーバーからクライアントまでのネットワークの遅延時間が問題になります。何回か遅延を測定しその平均時間で補正をします。NTP を応用したフリーソフトには、「Sakura Watch」などがあります。

    3. 謝辞

       ここでのプログラムは、小高氏の「Javaネットワークプログラミング」を利用しています。

    4. アンケート

      1:IPを知る方法は知っていましたか? 1:はい  2:聞いたことはある  3:いいえ
      2:ポート番号は知っていましたか? 1:はい  2:聞いたことはある  3:いいえ
      3:ネットワークプログラムに興味は? 1:大いにある 2:少しある 3:あまりない
      4:プログラムは動作しましたか? 1:できた 2:できない