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

                                       筑波大学 システム情報系 情報工学域
                                       新城 靖
                                       <yas@cs.tsukuba.ac.jp>

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

echoサーバ(Java版)

EchoServerSingle.java

main()

   1:	/*
   2:	        EchoServerSingle.java -- 文字列を送受信するサーバ(TCP/IP, Java版, スレッドなし)
   3:	        ~yas/syspro/ipc/EchoServerSingle.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:	            stderr.printf("Usage: %% java EchoServerSingle port\n");
  16:	            System.exit( -1 );
  17:	        }
  18:	        int portno = Integer.parseInt( argv[0] );
  19:	        echo_server_single( portno );
  20:	    }
  21:	
引数として、ポート番号を取る。

echo_server_single()

  22:	    public static void echo_server_single( int portno ) throws IOException
  23:	    {
  24:	        ServerSocket acc = new ServerSocket( portno );
  25:	        print_my_host_port( portno );
  26:	        while( true )
  27:	        {
  28:	            stdout.printf("accepting incoming connections (hash== %s) ...\n", acc.hashCode());
  29:	            Socket com = acc.accept();
  30:	            tcp_peeraddr_print( com );
  31:	            EchoServerWorker esw = new EchoServerWorker(com);
  32:	            esw.run();
  33:	        }
  34:	    }

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

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

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

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

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

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

print_my_host_port()

print_my_host_port() は、同じ同名のCの関数と同じ働きをする。
  35:	    public static void print_my_host_port( int portno ) throws UnknownHostException
  36:	    {
  37:	        InetAddress ia = java.net.InetAddress.getLocalHost();
  38:	        String hostname = ia.getHostName();
  39:	        stdout.printf("run telnet %s %d\n",hostname, portno );
  40:	    }

tcp_peeraddr_print()

tcp_peeraddr_print() は、同じ同名のCの関数と同じ働きをする。
  41:	    public static void tcp_peeraddr_print( Socket com )
  42:	    {
  43:	        InetSocketAddress isa = (InetSocketAddress)com.getRemoteSocketAddress();
  44:	        InetAddress ia = isa.getAddress();
  45:	        String peerhostaddr = ia.getHostAddress();
  46:	        int peerportno = isa.getPort();
  47:	        stdout.printf("connection (hash== %s) from %s:%d\n", com.hashCode(),
  48:	                      peerhostaddr, peerportno );
  49:	    }

stdoutとstderr

以下の変数 stdoutとstderr は、C 言語のプログラムに似せるために 定義している。
  50:	    static java.io.BufferedReader stdin = 
  51:	        new java.io.BufferedReader( new java.io.InputStreamReader(System.in) );
  52:	    static java.io.PrintStream stdout = System.out;
  53:	    static java.io.PrintStream stderr = System.err;     
  54:	}

EchoServerWorker

   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:	            InputStream in = com.getInputStream();
  21:	            OutputStream out = com.getOutputStream();
  22:	            String line;
  23:	            while( (line = readLineIS( in ))!= null )
  24:	            {
  25:	                stdout.printf("received (com hash==%s) %d characters, [%s]\n",
  26:	                              com.hashCode(), line.length(),line );
  27:	                printfOS( out, "%s", line );
  28:	            }
  29:	            stdout.printf("connection (com hash==%s) closed.\n", com.hashCode());
  30:	            in.close();
  31:	            out.close();
  32:	            com.close();
  33:	        }
  34:	        catch( IOException e )
  35:	        {
  36:	            stderr.println( e );
  37:	        }
  38:	    }
echoサービスのクライアント と同様に、 readLineIS() や printfOS() により メッセージの受信と送信を行っている。

実行例

サーバ側
$ cp ~yas/syspro/ipc/EchoServer{Single,Worker}.java . [←]
$ ls *java [←]
EchoServerSingle.java   EchoServerWorker.java
$ javac -encoding EUC-JP EchoServerSingle.java EchoServerWorker.java [←]
$ ls *class [←]
EchoServerSingle.class  EchoServerWorker.class
$ java EchoServerSingle  [←]
Usage: % java EchoServerSingle port
$ java EchoServerSingle 1231 [←]
run telnet cosmos30 1231
accepting incoming connections (hash== 2039470468) ...
connection (hash== 1813126941) from 130.158.86.170:49973
received (com hash==1813126941) 5 characters, [abc
]
received (com hash==1813126941) 5 characters, [def
]
received (com hash==1813126941) 5 characters, [ghi
]
connection (com hash==1813126941) closed.
accepting incoming connections (hash== 2039470468) ...
^C
$ []
クライアント側
$ telnet cosmos30 1231 [←]
Trying 130.158.86.170...
Connected to cosmos30.coins.tsukuba.ac.jp.
Escape character is '^]'.
abc[←]
abc
def[←]
def
ghi[←]
ghi
^]
telnet> quit[←]
Connection closed.
$ []

Last updated: 2012/05/28 16:27:41
Yasushi Shinjo / <yas@cs.tsukuba.ac.jp>