サーバーとはずっと待ち続けて、接続してきた相手の要求に応じて仕事をするソフトウェア、またはそのソフトウェアが動いているコンピュータのことを言います。
サーバーのやってくれる仕事をサービスということがあります。サービスを受けるホスト(またはユーザー)をクライアントといいます。
http:// で相手をしたのがウェブサーバー、file:// で相手をしたのがローカルなファイルシステム(またはファイルサーバー)です。
ネットワークの中で接続するサーバーを指定するのはホスト名あるいはIPアドレスです。しかし,これでは一つのホストが一つのサービスしかできません。
そこでポート番号を使います。ポート番号で管理することで複数の接続を同時に行う事ができます。
銀行の窓口やスーパーマーケットのレジには番号がついていますがそのようなイメージで考えてください。異なるポートに接続する事でちがったサービスを受けられます。たとえばウェブサーバーは80,メール送信は25などです。ポート番号は勝手に使うと通信できなくなりますから,多くの人が使うサービスについてはよく使われているものにみんなが合わせて使っているうちに番号が固定されてきました。これをウェルノウンポート(Well known port:よく知られたポート)と呼んでいます。
ポート番号は、0から 65535までの範囲ですが,一般のユーザーのプログラムでは1024よりも大きなものを使うことになっています。他のソフト(サービス)が使っているものと重ならないようにします。
接続してきたら,メッセージを返すだけというサーバーを作ります。このサーバーは 10001 ポートで待ちます。
/** Socketでメッセージを送信するサーバー */ import java.net.*; import java.io.*; import java.util.*; public class Server01 { public static void main( String[] args ) { /** 使用するポート番号 */ int PORT = 10001; /** 応答するメッセージ(3つの文字列の配列) */ String message[] = { "## Welcome to My Server " , "## This is hoge " , "## local host is vineXX " }; try { ServerSocket mysvsoc = new ServerSocket( PORT ); while( true ) { //クライアントが接続してくるまで待ち続ける System.out.println( "waiting..." ); Socket mysoc = mysvsoc.accept(); //接続があると、Socketクラスのオブジェクトを返す // 接続されたことを画面に表示 String remotehost = mysoc.getInetAddress().getHostName(); Date date = new Date(); System.out.println(date.toString() + ":" + remotehost); // メッセージ送信 OutputStreamWriter out = new OutputStreamWriter(mysoc.getOutputStream()); BufferedWriter outb = new BufferedWriter(out); for( int i=0; i<message.length; i++ ) { outb.write( message[i], 0, message[i].length() ); outb.newLine(); } outb.close(); out.close(); mysoc.close(); } } catch( SocketException e ) { System.err.println("Socket Error"); } catch( IOException e ) { System.err.println("IO Error"); } catch( Exception e ){ System.out.println(e.toString()); } } }
実行すると次のようになって接続を待ちます
$ java Server01 waiting...
まだサーバーしかなく接続してくるクライアントはありませんのでいつまでもこのままです。しかし,接続があると次のように時刻と相手のホスト名を表示し,また待機状態に戻ります。
$ java Server01 waiting... Tue Dec 9 12:50:51 JST 2008:vineXX.st.seiai.ed.jp waiting...
このサーバーを停止するには ctrl+c (コントロールキーを押しながらc)で止めます。
これはしばらく止めずにおいて,もう一つ端末(コンソール)を起動してクライアントを作りましょう。
端末の起動はもう一度javaを選ぶのが手軽です。アクセサリから端末を起動した場合はカレントディレクトリを移す必要があります。
今度はポート 1001 で接続するクライアントです。サーバーと同じポートを使う事で接続できます。
このクライアントは接続したらサーバーが送ってくるメッセージを画面に表示します。
$ cd user/java
/** Socketでメッセージを受信するクライアント */ import java.io.*; import java.net.*; public class Client01 { public static void main( String[] args ) { /** 通信に使用するポート番号 */ int PORT = 10001; try { Socket mysoc = new Socket( args[0], PORT ); InputStreamReader in = new InputStreamReader(mysoc.getInputStream()); BufferedReader inb = new BufferedReader(in); String line; while( ( line = inb.readLine() ) != null ){ System.out.println( line ); } inb.close(); in.close(); mysoc.close(); } catch( ArrayIndexOutOfBoundsException e ) { System.err.println("Usage:java Client01 hostname"); } catch( UnknownHostException e ) { System.err.println( "Host not found" ); } catch( SocketException e ) { System.err.println("Socket Error"); } catch( IOException e ) { System.err.println("IO Error"); } } }
実行する時には、
$ java Client01 vineXX
というように引数として自分の使っているコンピュータのホスト名、あるいはIPアドレスを入れます。
実行すると次のようにサーバーからのメッセージを表示します。
$ java Client01 vineXX ## Welcome to My Server ## This is hoge ## local host is vineXX
もちろんこのホスト名にサーバープログラムが動いている他のホストの名前を入れればそちらから応答があります。
host vine41 から vine41 にアクセス
hoge@vine41:~/user/java$ java Client01 vine41 ## Welcome to My Server ## This is hoge ## local host is vine41 hoge@vine41:~/user/java$
host vine39 から vine41 にアクセス
foo@vine39:~/user/java$ java Client01 vine41 ## Welcome to My Server ## This is foo ## local host is vine39 foo@vine41:~/user/java$
サーバーの方には次のようにアクセス記録が出ます。
hoge@vine41:~/user/java$ java Server01 waiting... Mon Dec 08 21:16:49 JST 2008:vine41.st.seiai.ed.jp waiting... Mon Dec 08 21:20:36 JST 2008:vine39.st.seiai.ed.jp waiting...