ヒント
以下の2つのスレッドを作成するとシンプルに作成できます.
// 入出力ストリームを使うので,java.io.* を import import java.io.*; // ソケットを使うので java.net.* を import import java.net.*; // ユーザのキーボード入力を受け取って, サーバに送るスレッドと // サーバと接続したソケットからメッセージを受け取って表示するスレッド // を共用したクラス class Sender implements Runnable{ // 入力ストリーム BufferedReader in; // 出力ストリーム PrintWriter out; // コンストラクタ public Sender(BufferedReader in,PrintWriter out){ // 左辺の this.inはインスタンス変数の in, 右辺の inは引数の in this.in=in; this.out=out; } // スレッドが start すると,これが呼ばれて,動き続ける. public void run(){ try{ String s; // 入力ストリームから一行入力 while((s=in.readLine())!=null){ // 出力ストリームに一行出力 out.println(s); } } catch(Exception e){ System.err.println(e); } } } // java ChatClient ホスト名 ポート番号 // と呼び出す class ChatClient{ public static void main(String args[]){ // ホスト名 String hostName=args[0]; // ポート番号を文字列から整数に変換 int port=Integer.parseInt(args[1]); try{ // サーバと接続するためのソケットを作る Socket sock=new Socket(hostName,port); // ユーザからの入力のストリームの作成 BufferedReader ui=new BufferedReader(new InputStreamReader(System.in)); // サーバからの入力のストリームの作成 BufferedReader si=new BufferedReader(new InputStreamReader(sock.getInputStream())); // サーバへの出力のストリームの作成 PrintWriter so=new PrintWriter(sock.getOutputStream(),true); // ユーザへの出力のストリームの作成 PrintWriter uo=new PrintWriter(System.out,true); // ユーザ入力をサーバに転送するスレッドを作成し, start new Thread(new Sender(ui,so)).start(); // サーバからの入力をユーザに転送するスレッドを作成し, start new Thread(new Sender(si,uo)).start(); } catch(IOException e){ System.err.println(e); } } }
このプログラムはあらかじめ,サーバを動かしておいてから実行する必要が あるが,その説明を読んでないもの,自分の作成したプログラムなのに実行時 の引数がわかっていないもの,実際に実行したとは思えない解答もあった.
変わった解答例としては,入力をSystem.inから行わずに Frameの中の TextFieldから行うことで,スレッドを作らずに実現しているものもあった.
詳細
オプション課題
プロトコル(1行単位) 1. 「ユーザ名の登録」 サーバ -> クライアント: Name: クライアント -> サーバ: ユーザ名(ここで嘘をつくと,勝っても正しく記録されない) 2. 「ゲームのスタート」 サーバ -> クライアント: Start 対戦相手 3. 「出す手を伝える」 クライアント -> サーバ: 手(0,1,2) サーバ -> クライアント: 相手の手 4. 50回の勝負が終わる前は3に戻る. 5. 結果の表示 サーバ -> クライアント :ヒント
プロトコルを理解するために,
telnet ux019 3331とやって,名前の入力,[0-2]の数字の入力をやってみるのも良い.
// Randomクラスを利用するので java.util.*をインポート import java.util.*; // 入出力関係のクラスを利用するので java.util.*をインポート import java.io.*; // ネットワーク関係のクラスを利用するので java.net.*をインポート import java.net.*; class Kodomo{ static void janken(BufferedReader in,PrintWriter out) throws IOException{ Random r=new Random(); String s=in.readLine(); System.out.println("対戦相手は"+s); int index=(int)(r.nextDouble()*3.0); for(int i=0;i< 50;i++){ out.println(index); // サーバからの入力をもらう if((s=in.readLine())==null){ System.exit(1); } index=(Integer.parseInt(s)+2)%3; } while((s=in.readLine())!=null) System.out.println(s); } public static void main(String args[]) throws IOException{ if(args.length<2) usage(); Socket sock=new Socket(args[0],Integer.parseInt(args[1])); BufferedReader in=new BufferedReader(new InputStreamReader(sock.getInputStream())); PrintWriter out=new PrintWriter(sock.getOutputStream(),true); out.println(System.getProperty("user.name")); janken(in,out); } static void usage(){ System.err.println("使い方: java Janken ホスト名 ポート番号"); System.exit(1); } }相手の直前に出した手に勝てる手を出す(0に勝つのは2,1に勝つのは0,2に勝 つのは1だが,これは 相手の出した手に2を足して 3 で割った余り)プログラム. プロトコルが決まっているので,スレッドを使う必要はない.
// Randomクラスを利用するので java.util.*をインポート import java.util.*; // 入出力関係のクラスを利用するので java.util.*をインポート import java.io.*; // ネットワーク関係のクラスを利用するので java.net.*をインポート import java.net.*; class Otona2{ static void janken(BufferedReader in,PrintWriter out) throws IOException{ Random r=new Random(); String s=in.readLine(); System.out.println("対戦相手は"+s); // 1手目は乱数で 0-2を生成する int index=(int)(r.nextDouble()*3.0),index1; // ゲームの記録を取る int[][] rec=new int[2][50]; for(int i=0;i< 50;i++){ out.println(index); // サーバからの入力をもらう if((s=in.readLine())==null){ System.exit(1); } // 相手の出した手を整数に直す index1=Integer.parseInt(s); // 記録する rec[0][i]=index; rec[1][i]=index1; // 2手目は相手の1手目に勝つものを出す. if(i==0) index=(index1+2)%3; else{ int hist[]=new int[3]; // 過去に,1手前の自分の手,相手の手と一致する回の次に相手が何を // 出したかの頻度を計算する. for(int j=0;j< i;j++){ if(rec[0][j]==rec[0][i] && rec[1][j]==rec[1][i]) hist[rec[1][j+1]]++; } // 同じ状況で相手が0を多くだしていた時は2を出す if(hist[0]>=hist[1] && hist[0]>=hist[2]) index=2; // 同じ状況で相手が1を多くだしていた時は0を出す else if(hist[1]>=hist[2]) index=0; // 同じ状況で相手が2を多くだしていた時は1を出す else index=1; } } while((s=in.readLine())!=null) System.out.println(s); } public static void main(String args[]) throws IOException{ if(args.length< 2) usage(); Socket sock=new Socket(args[0],Integer.parseInt(args[1])); BufferedReader in=new BufferedReader(new InputStreamReader(sock.getInputStream())); PrintWriter out=new PrintWriter(sock.getOutputStream(),true); out.println(System.getProperty("user.name")); janken(in,out); } static void usage(){ System.err.println("使い方: java Otona2 ホスト名 ポート番号"); System.exit(1); } }
g840587 g840593 g920024 g940029 g940124 g940151 g940186 g940199 g940229 g940250 g940288 g940299 g940347 g940349 g940351 g940566 g940573 g940578 g940600 g940604 g940620 g940622 g940645 g940795 g940811 g940821 g940845 g940846 g940851 g940859 g940868 g940869 g940889 g940893 g941042 g941059 g941140 g941267 g950060 g950112 g950134 g950171 g950241 g950245 g950433
g940029 g940299 g940349 g940573 g940578 g940645 g940893 g950112 g950433