システムプログラム(第7週): echoサーバ(Java版)

                                       筑波大学 システム情報工学研究科 
                                       コンピュータサイエンス専攻, 電子・情報工学系
                                       新城 靖
                                       <yas@is.tsukuba.ac.jp>

このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~syspro/2007/No7_files/echo-server-java.html
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~syspro/2007/
http://www.coins.tsukuba.ac.jp/~yas/

echoサーバ(Java版)

   1:	/*
   2:	        EchoServerSingle.java -- 文字列を送受信するサーバ(TCP/IP, Java版, スレッドなし)
   3:	        ~yas/syspro/ipc/EchoServer.java
   4:	        Created on 2004/05/09 20:00:24
   5:	*/
   6:	
   7:	import java.net.*;
   8:	import java.io.*;
   9:	
  10:	class EchoServerSingle
  11:	{
  12:	    public static void main(String argv[]) throws IOException {
  13:	        if( argv.length != 1 )
  14:	        {
  15:	            System.err.println("Usage: % java EchoServer port");
  16:	            System.exit( -1 );
  17:	        }
  18:	        int portno = Integer.parseInt( argv[0] );
  19:	        echo_server( portno );
  20:	    }
引数として、ポート番号を取る。

  22:	    public static void echo_server( int portno ) throws IOException
  23:	    {
  24:	        ServerSocket acc = new ServerSocket( portno );
  25:	        print_my_host_port( portno );
  26:	        while( true )
  27:	        {
  28:	            Socket com = acc.accept();
  29:	            tcp_peeraddr_print( com );
  30:	            EchoServerWorker esw = new EchoServerWorker(com);
  31:	            esw.run();
  32:	        }
  33:	    }

ServerSocket() は、引数で与えられたポート番号を使って接続要求受付用ポー トを作成し、それに対応した ServerSocket のオブジェクトを返す。このオブ ジェクトは、Socket のオブジェクトとは異なり、そまままでは通信に用いる ことはできない。

print_my_host_port() は、telnet で接続する時のヒントを表示する。

サーバのプログラムの特徴は、内部に無限ループを持っていることである。 サーバは、普通の状態では、終了しない。

accept() は、接続要求を待つメソッドである。クライアントから接続が来る まで、システムコールを実行したまま止まっているように見える。接続要求が 届くと、TCP/IP通信路の開設され、通信用ポートに対応したSocket クラスの オブジェクトが返される。このオブジェクトは、クライアント側と同様に getInputStream() や getOutputStream() により入出力可能なストリームを作 り出すことができる。

tcp_peeraddr_print() は、通信相手の IP アドレスとポート番号を表示する関数である。

以後の仕事は、EchoServerWorker というクラスのオブジェクトを作成して行 わせている。コンストラクタでは、Socket クラスのオブジェクトを渡してい だけであり、実際の処理は何も行われない。実際の処理は、run() メソッドで 行われる。

  34:	    public static void print_my_host_port( int portno ) throws UnknownHostException
  35:	    {
  36:	        InetAddress ia = java.net.InetAddress.getLocalHost();
  37:	        String hostname = ia.getHostName();
  38:	        stdout.println("run telnet "+hostname+" "+portno );
  39:	    }
  40:	    public static void tcp_peeraddr_print( Socket com )
  41:	    {
  42:	        InetSocketAddress isa = (InetSocketAddress)com.getRemoteSocketAddress();
  43:	        InetAddress ia = isa.getAddress();
  44:	        String peerhostaddr = ia.getHostAddress();
  45:	        int peerportno = isa.getPort();
  46:	        stdout.println("connection (hash=="+com.hashCode()+") from "+peerhostaddr+":"+peerportno );
  47:	    }

print_my_host_port() と tcp_peeraddr_print() は、それぞれ同じ同名のC の関数と同じ働きをする。

   1:	/*
   2:	        EchoServerWorker.java -- 文字列を送受信するサーバ/ワーカ(TCP/IP, Java版)
   3:	        ~yas/syspro/ipc/EchoServerWorker.java
   4:	*/
   5:	
   6:	import java.net.*;
   7:	import java.io.*;
   8:	
   9:	public class EchoServerWorker implements Runnable
  10:	{
  11:	    Socket com ;
  12:	    EchoServerWorker( Socket com )
  13:	    {
  14:	        this.com = com ;
  15:	    }
  16:	    public void run()
  17:	    {
  18:	        try
  19:	        {
  20:	            BufferedReader in = new BufferedReader(
  21:	                new InputStreamReader( com.getInputStream() ));
  22:	            PrintStream out = new PrintStream( com.getOutputStream() );
  23:	            String line;
  24:	            while( (line = in.readLine())!= null )
  25:	            {
  26:	                stdout.println("received (hash=="+com.hashCode()+") "+
  27:	                               line.length()+" characters, ["+line+"]");
  28:	                out.println( line );
  29:	            }
  30:	            stdout.println("connection (hash=="+com.hashCode()+") closed.");
  31:	            in.close();
  32:	            out.close();
  33:	            com.close();
  34:	        }
  35:	        catch( IOException e )
  36:	        {
  37:	            stderr.println( e );
  38:	        }
  39:	    }
  40:	    static java.io.BufferedReader stdin = 
  41:	        new java.io.BufferedReader( new java.io.InputStreamReader(System.in) );
  42:	    static java.io.PrintStream stdout = System.out;
  43:	    static java.io.PrintStream stderr = System.err;     
  44:	}
echoサービスのクライアント と同様に、Socket から BufferedReader と PrintStream を生成している。 そして、readLine() や println() により通信を行っている。

スレッドによる複数のクライアントに対するサービスの同時提供(Java版)

わずかな修正で、スレッド対応になり、複数のクライアントに対してサービス を同時に提供できるようになる。
 % diff -c EchoServerSingle.java EchoServer.java
 *** EchoServerSingle.java       Sun May  9 20:01:50 2004
 --- EchoServer.java     Sun May  9 19:59:33 2004
 ***************
 *** 1,13 ****
   /*
 !       EchoServerSingle.java -- 文字列を送受信するサーバ(TCP/IP, Java版, スレッドなし)
	 ~yas/syspro/ipc/EchoServer.java
 !       Created on 2004/05/09 20:00:24
   */

   import java.net.*;
   import java.io.*;

 ! class EchoServerSingle
   {
       public static void main(String argv[]) throws IOException {
	 if( argv.length != 1 )
 --- 1,13 ----
   /*
 !       EchoServer.java -- 文字列を送受信するサーバ(TCP/IP, Java版)
	 ~yas/syspro/ipc/EchoServer.java
 !       Created on 2004/02/14 16:22:13
   */

   import java.net.*;
   import java.io.*;

 ! class EchoServer
   {
       public static void main(String argv[]) throws IOException {
	 if( argv.length != 1 )
 ***************
 *** 27,34 ****
	 {
	     Socket com = acc.accept();
	     tcp_peeraddr_print( com );
 !           EchoServerWorker esw = new EchoServerWorker(com);
 !           esw.run();
	 }
       }
       public static void print_my_host_port( int portno ) throws UnknownHostException
 --- 27,34 ----
	 {
	     Socket com = acc.accept();
	     tcp_peeraddr_print( com );
 !           Thread th = new Thread( new EchoServerWorker(com) );
 !           th.start();
	 }
       }
       public static void print_my_host_port( int portno ) throws UnknownHostException
 % 

Last updated: 2007/05/29 18:23:02
Yasushi Shinjo / <yas@is.tsukuba.ac.jp>