//NetBull.java -- by tsaiwn@csie.nctu.edu.tw //javac NetBull.java CowBull.java //java NetBull [port_number] //then you can use "telnet hostName 3388" to connect this program //where "hostName" is the IP of the machine running this program //Note that there are 3 classes in 2 Java files. import java.io.*; import java.net.*; import java.util.*; // Date is in this package import java.lang.*; // NOT necessary class NetBull { // should be same as the File name, 建議該用大寫開頭 NetBull ServerSocket ssk; // 用來等待連線 Socket ccc; // 有client連線進來可生出 Socket 小洞 static final int DEFAULT_PORT = 3388; // final 表示後面不可改它 static final int TIME_OUT = 300; // 300 seconds int portNumber; // Listening TCP port PrintStream cout = System.out; // 取個與 C++ 同名的物件變數 PrintStream cerr = System.err; static int getPortNumber(String str) { // get port number on command line int ans=0; //For example: java NetBull 5678 try { ans = Integer.parseInt(str); }catch(Exception e) { ans = 0; } return ans; } // getPortNumber if any, for example: java NetBull 5566 /// public static void main(String[ ] xxx) throws Exception { // ^^^ 宣稱若出問題會報告馬政府 ! int pp=0; // 若 command line 有 port number 就用它 if(xxx.length > 0) pp =getPortNumber(xxx[0]); // array 的 length if(pp==0) pp = DEFAULT_PORT; // 有打就用 user 打的, 否則用原來的 checkFiles( ); // 若有錯就不會回來 :-) // OK now new NetBull(pp); // 要求生出 NetBull 物件並立刻執行 NetBull( ) 建構子 }// main /// NetBull(int port) { // 這就是 class NetBull 的 建構子 constructor portNumber = port; cout.println("Server started ... at port "+ portNumber); try { ssk = new ServerSocket( portNumber ); ssk.setSoTimeout(TIME_OUT * 1000); // time out = 300 sec ? while(38 == 38) { // true try { ccc = ssk.accept( ); // 等待連線後取得連線暫稱 ccc (Socket) }catch(Exception e) { cerr.println(" .. " + TIME_OUT/60.0 // to minutes + " minutes past "+new Date( )); continue; // to Listen again } Brother b = new Brother(ccc); // 請一個小弟, 並把連線ccc交給小弟 b.start( ); // 叫小弟開始處理該連線, 執行它的 run( ) } // while } catch(Exception e) { cerr.println("Cannot startup ChatServer at port "+ portNumber+"!"); cerr.println("Maybe port "+portNumber+" has been used?\nByebye."); }//try cerr.println("...Server stop."); } // NetBull( ) static void checkFiles( ) { // 確定有 Brother.class 和 CowBull.class try{ Class cc = Class.forName("CowBull"); // try to find CowBull.class }catch(Exception e) { System.err.println(" ? can NOT find CowBull.class!"); System.exit(38); } // try try{ Class cc = Class.forName("Brother"); // try to find Brother.class }catch(Exception e) { System.err.println(" can NOT find Brother.class!"); System.exit(49); } }// checkFiles } // class NetBull /// /// // class Brother extends Thread { // 表示這 class Brother 是一個 Thread 執行緒 Socket mySock; // 用來記住"老大"傳來的資料, 方便class內其他 function 用 Date now; String name="???"; // remember user name private String remoteAddr; // 連著我的對方 IP address private BufferedReader in; private PrintStream out; private PrintStream cout = System.out; // cout 只是簡寫而已 Brother(Socket x) { mySock = x; now = new Date( ); try { remoteAddr = mySock.getInetAddress().getHostAddress(); // 然後取得InputStream並包成 BufferedReader 方便 readLine() in = new BufferedReader( new InputStreamReader(mySock.getInputStream()) ); // 再取得 OutputStream 並包成 PrintStream out = new PrintStream( (mySock.getOutputStream()), true ); cout.println("Friend from " + remoteAddr + " at " + now); } catch(Exception e) { // say something } } // Brother constructor public void run( ) { // 規定 Thread 內一定要這樣寫才有用 name = "???"; // 接著, 可以 與 連線者對話 try { out.print("Welcome to the BullCow Land.\n\r"); out.flush( ); while(name.length( ) < 7) { // 不可能短於 7 :-) out.print("Give me your 學號與姓名: "); name = in.readLine( ); name = edit(name); // 去除 Backspace 等等 .. if(name.startsWith(" ") || name.length( )< 6 ) { name = "???"; out.print(" ! 不可亂打, "); }//if }//while //cout.println(" length=" + name.length( ) +" Name: "+name); out.println("歡迎 " + name + " 開始玩, 公牛是位置對數字也對!\n\r"); cout.println(" " + name + " from " + remoteAddr + " play on " + new Date( ) +".."); log(1, name + " @" + remoteAddr+" " + new Date( ) ); // 1=start CowBull cb = new CowBull(in, out, this); // this 指到我 ! /// cb.play( ); // auto play in constructor out.println("\n\rBye bye " + name + " at " + new Date( )); log(0, name + " leaving " + new Date( ) ); // 0= leaving try { mySock.close( ); }catch(Exception e) { } }catch(Exception e) { } cout.println(" " + name + " leaving at " + new Date( ) +"..."+ remoteAddr); } // run( ) String edit(String str) { // 去掉 str 中的 Backspace 以及前後空白 String t = str.trim( ); char u[ ] = t.toCharArray( ); // 轉為 char array [ ] int p = 0; int last = u.length; for(int i=0; i< last; ++i) { // 127 為 DEL key 一般不處理 //System.out.print(" :" + (int)u[i]); if((int)u[i] == 127){ continue; } // DEL key if((int)u[i] == 8) { if(p>=0) --p; if(p>=0) --p;} //Backspace else u[p] = u[i]; // copy 到左邊尾巴 ++p; } while(p < last) u[p++] = ' '; // 後面都清掉 t = new String(u); // 轉回 String return t.trim( ); // 再去掉頭尾空白 }//edit void log(int k, String s) { // Log into log file FileWriter f; PrintWriter logFile; try { f = new FileWriter(logFileName, true); // true to Append logFile = new PrintWriter(f); }catch(Exception e) { return; } // No log if fail if(k == 0) logFile.append(" .."); // leaving logFile.append(s +"\r\n"); logFile.close( ); return; } // log String logFileName = "bullLog.txt"; // FileName of the Log file } // class Brother