複数のクライアントを処理する


サーバ

ServerSocketから新しい接続を受け付ける度に、新しいスレッドを生成し、 そのスレッドにクライアントの処理を任せます。

スレッドを使うには、Threadのサブクラスを生成するか、またはRunnableを implementsしたクラスが必要になります。 ここでは EchoHandlerというRunnableをimplementsしたクラスを作成しています。












RunEchoServerThreaded.javaの実行例(サーバ)
$ javac RunEchoServerThreaded.java  ←コンパイル
$ java RunEchoServerThreaded  ←実行
accept from /127.0.0.1
0: starts Socket[addr=/127.0.0.1,port=3534,localport=8888]
0: data=abc
accept from /127.0.0.1
1: starts Socket[addr=/127.0.0.1,port=3563,localport=8888]
1: data=123
1: closed Socket[addr=/127.0.0.1,port=3563,localport=8888]
1: ended
accept from /127.0.0.1
2: starts Socket[addr=/127.0.0.1,port=3567,localport=8888]
2: data=AAAA
0: data=def
2: data=BBB
2: closed Socket[addr=/127.0.0.1,port=3567,localport=8888]
2: ended
0: data=ghi
0: closed Socket[addr=/127.0.0.1,port=3534,localport=8888]
0: ended


実行例(クライアント0)
$ java RunTelnetClient localhost 8888  ←入力
Trying 127.0.0.1...
Connected to cezanne.
Escape character is '^]'.
abc  ←入力
abc
def  ←入力
def
ghi  ←入力
ghi
←WindowsではControlキーとZキーを同時に押すと入力終了(LinuxではControl+D)
Connection closed by foreign host.


実行例(クライアント1)
$ RunTelnetClient localhost 8888  ←入力
Trying 127.0.0.1...
Connected to cezanne.
Escape character is '^]'.
123  ←入力
123
←WindowsではControlキーとZキーを同時に押すと入力終了
Connection closed by foreign host.


実行例(クライアント2)
$ java RunTelnetClient localhost 8888  ←入力
Trying 127.0.0.1...
Connected to cezanne.
Escape character is '^]'.
AAAA  ←入力
AAAA
BBB  ←入力
BBB
←WindowsではControlキーとZキーを同時に押すと入力終了
Connection closed by foreign host.

記述について

上記の EchoServerThreaded.javaはわかりやすさを優先して書いたものです。 個々のクライアントを生成する部分は、3行をまとめて1行で書くことができます。

EchoServerThreaded.javaの別の書き方
import java.net.*;
import java.io.*;
public class EchoServerThreaded {
    ServerSocket servSock;
    public EchoServerThreaded(int port) {
	try {
	    servSock = new ServerSocket(port);
	} catch (IOException e) {
	    System.err.println("IO Error " + e);
	}
    }
    public void run() {
	Socket sock = null;
	int id=0;
	try {
	    while (true) {
		sock = servSock.accept();
		System.out.println("accept from "+sock.getInetAddress());
		new Thread(new EchoHandler(sock,id++)).start();
	    }
	} catch (IOException e) {
	    System.err.println("IO Error " + e);
	}
    }
}


コンピュータネットワーク 演習


提出〆切は次回の授業の開始時間です。


課題5a

提出先 http://ynitta.com/class/network/local/handin/list.php?id=kadai5a
提出ファイルMyServerHandler.java
コメント欄 "400 Error\r\n"や "200 Ok\r\n"+「ファイルの内容」 が送られてきている状況を表すクライアント側の実行例。 コピー&ペーストで貼りつけて置くこと。

8888番ポートで接続を受け付けるサーバを作成しなさい。 クライアント毎にスレッドを生成して、クライアントからの要求を処理すること。

クライアントからの要求は、行単位で送られてくる。 行は1個以上の空白文字で区切られた文字列から構成されている。 クライアントから送られてきた行が 複数行送られてきた場合は、サーバはそれらの行数分だけ処理を繰り返す。

このサーバは簡単のためにセキュリティを高める工夫はしていません。 本来は、特定のフォルダ(ディレクトリ)よりも上の階層のファイルには アクセスできないように、

などの「サニタイジング(sanitizing)」(=入力データから 危険な文字列を別の文字列に置き換えて「無害化」すること) 処理が必要となります。













RunMyServer.javaの実行例(サーバ)
$ java RunMyServer 
accept from /127.0.0.1
0: starts Socket[addr=/127.0.0.1,port=36472,localport=8888]
0: data=abc
0: data=def
0: data=get RunMyServer.java
0: closed Socket[addr=/127.0.0.1,port=36472,localport=8888] ←Clientが終了した
0: ended


RunTelnetClient.javaの実行例(クライアント)
$ java RunTelnetClient localhost 8888 
abc 
400 Error

def 
400 Error

get RunMyServer.java 
200 Ok
public class RunMyServer {
    public static void main(String[] args) {
        int port=8888;
        if (args.length > 0) port = Integer.parseInt(args[0]);
        MyServer serv = new MyServer(port);
        serv.run();
    }
}

     ←Ctl-Cで終了