- 3.1. サーバソケット
- 3.2. 複数の通信を受け付けるサーバ
3.2. 複数の通信を受け付けるサーバ
前項では簡単なサーバアプリケーションを作成しました。しかし、このサーバには大きな欠点があります。このサーバは同時に1つのクライアントとしか通信できません(試しに複数のウィンドウを開いて、EchoClientを同時に2つ起動してみて下さい)。このサーバは最初の接続要求を受け付けたときに、acceptメソッドが終了してしまいます。acceptメソッドが呼び出されていないと、サーバは接続の受け付けができない点に注意してください。
それでは、acceptメソッドを繰り返し呼び出せばよいのでしょうか。
while(true){ Socket socket = serverSocket.accept(); }
これだと、複数の接続を受け付けることはできますが、acceptメソッドでプログラムの実行がブロックされてしまうので、ソケットを利用したメッセージの送受信ができなくなってしまいます。クライアントからの接続要求を待ち続けながら、同時に接続済みのソケットで通信を行うにはどうすればよいでしょうか。これまで学習した内容を思い出して下さい。そう、スレッドを利用するのです。
while(true){ Socket socket = serverSocket.accept(); Thread thread = new EchoThread(socket); thread.start(); }
このように、クライアントからの接続があるたびに、通信処理を行うスレッドを作成して起動します。クライアントとの通信処理は新しいスレッドに任せ、もともとのプログラムの処理はループによって再びaccept()メソッドの実行に戻ります。
次のリストは、複数の接続を受け付けるサーバアプリケーションです。
リスト MultiEchoServer.java
import java.net.Socket; import java.net.ServerSocket; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.IOException; public class MultiEchoServer { public static final int ECHO_PORT = 10007; public static void main(String args[]) { ServerSocket serverSocket = null; try { serverSocket = new ServerSocket(ECHO_PORT); System.out.println("EchoServerが起動しました(port=" + serverSocket.getLocalPort() + ")"); while (true) { Socket socket = serverSocket.accept(); new EchoThread(socket).start(); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (serverSocket != null) { serverSocket.close(); } } catch (IOException e) {} } } } class EchoThread extends Thread { private Socket socket; public EchoThread(Socket socket) { this.socket = socket; System.out.println("接続されました " + socket.getRemoteSocketAddress()); } public void run() { try { BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter out = new PrintWriter(socket.getOutputStream(), true); String line; while ( (line = in.readLine()) != null ) { System.out.println(socket.getRemoteSocketAddress() + " 受信: " + line); out.println(line); System.out.println(socket.getRemoteSocketAddress() + " 送信: " + line); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (socket != null) { socket.close(); } } catch (IOException e) {} System.out.println("切断されました " + socket.getRemoteSocketAddress()); } } }
MultiEchoServerクラスは、ServerSocketによりクライアントからの接続を受け付けるクラスです。18〜21行目のループ処理で、クライアントからの接続を受け付けて、接続があると新しいスレッドを起動しています。
while (true) { Socket socket = serverSocket.accept(); new EchoThread(socket).start(); }
EchoThreadクラスは、クライアントとの通信処理を行うスレッドです。コンストラクタで通信に利用するSocketを設定します。このクラスはThreadを継承していますので、並列に実行したい通信処理はrun()メソッド内に記述します。run()メソッド内には、EchoServerクラスで記述した通信処理とほぼ同じコードを記述します。
このサーバアプリケーションを実行すると次のようになります。2つのクライアントからの処理を同時に処理していることが確認できます。
(実習課題1)
前節の演習課題で利用したDaytimeサーバを実装してください。送り返す文字列は、現在の日時がわかるものであれば書式は問いません。待ち受けポートは10013番ポートとして下さい。このプログラムはすぐにコネクションを開放するため、マルチスレッドにする必要性は高くありませんが、ここでは接続のあるたびに新しいスレッドを作成するようにして下さい。
また、前節の演習課題で作成したクライアントプログラムを変更し、コマンドラインで接続先ポート番号も指定できるようにして下さい。ポート番号は省略可とし、省略時のポート番号は13として下さい。このプログラムを用いて、サーバプログラムの動作を確認してください。