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

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

このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~syspro/2018/2018-05-30 /echo-server-java.html
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~syspro/2018/
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:	        {
  14:	                if( argv.length != 1 )
  15:	                {
  16:	                        stderr.printf("Usage: %% java EchoServerSingle port\n");
  17:	                        System.exit( -1 );
  18:	                }
  19:	                int portno = Integer.parseInt( argv[0] );
  20:	                echo_server_single( portno );
  21:	        }
  22:	
引数として、ポート番号を取る。

echo_server_single()

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

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の関数と同じ働きをする。
  37:	        public static void print_my_host_port( int portno )
  38:	                throws UnknownHostException
  39:	        {
  40:	                InetAddress ia = java.net.InetAddress.getLocalHost();
  41:	                String hostname = ia.getHostName();
  42:	                stdout.printf("run telnet %s %d\n", hostname, portno );
  43:	        }

tcp_peeraddr_print()

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

stdoutとstderr

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

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() // echo_receive_request_and_send_reply()
  17:	        {
  18:	                try
  19:	                {
  20:	                        InputStream in = com.getInputStream();
  21:	                        OutputStream out = com.getOutputStream();
  22:	                        String line;
  23:	                        while( (line = echo_receive_request(in))!= null )
  24:	                        {
  25:	                                stdout.printf("received (com hash==%s) %d characters, [%s]\n",
  26:	                                              com.hashCode(), line.length(),line );
  27:	                                echo_send_reply( line, out );
  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.printf("%s\n", e );
  37:	                }
  38:	        }
  39:	        public static String echo_receive_request( InputStream is ) 
  40:	                throws IOException
  41:	        {
  42:	                return( readLineIS(is) );
  43:	        }
  44:	        public static void echo_send_reply( String line, OutputStream out )
  45:	                throws IOException
  46:	        {
  47:	                printfOS( out, "%s", line );
  48:	        }
  49:	
echoサービスのクライアント と同様に、 readLineIS() や printfOS() により メッセージの受信と送信を行っている。

実行例

サーバ側
$ cp ~yas/syspro/ipc/EchoServer{Single,Worker}.java . [←]
$ ls *java [←]
EchoServerSingle.java   EchoServerWorker.java
$ javac EchoServerSingle.java EchoServerWorker.java [←]
$ ls *class [←]
EchoServerSingle.class  EchoServerWorker.class
$ java EchoServerSingle  [←]
Usage: % java EchoServerSingle port
$ java EchoServerSingle 1231 [←]
run telnet crocus30.coins.tsukuba.ac.jp 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 crocus30 1231 [←]
Trying 130.158.86.170...
Connected to crocus30.coins.tsukuba.ac.jp.
Escape character is '^]'.
abc[←]
abc
def[←]
def
ghi[←]
ghi
^]
telnet> quit[←]
Connection closed.
$ []

Last updated: 2018/05/27 17:02:19
Yasushi Shinjo / <yas@cs.tsukuba.ac.jp>