請注意: 做好習題請到 e3.nctu.edu.tw 本題討論版依照規定 post,
  且 Subject: LAB08 from 學號姓名
  報告的內容是心得, 其他包括Running Script 與帶Line numbers程式碼當夾檔副件
LAB08 身分證驗證程式 網路版 (簡易網路程式) 
Due: 2011/04/24 Sunday 23:59   (提醒2011/04/18期中考!)
 (考量大家期中考;本題 Due 延期到2011/04/30 Saturday 23:59)
Description:
      本題是要把 LAB04 的 Java Application 改成網路版(用你寫版本來改),
      配合我給的 TCP Server 範例 HaLa.java 稍作修改, 搭配執行,
      以便讓全世界的台灣人可以用 telnet 命令連進來 check 其身分證,
      或是生出一個可用的身分證號碼 :-)

Purpose: 藉由修改 HaLa.java 搭配 ID check 的 Java Application 成為
       網路版的 ID check 程式, 了解網路程式TCP Server 在 Java有多簡單:-)
       (HaLa.java 是一支典型的 TCP Server 程式; 以後很有用!:)

Hint:   
      ( 其實這題很簡單 :-! 大部份同學應該 10 分鐘內可以改完:-) 
   (1) 用 fc (或 diff) 比較 HaLa.java 與 HaLaBat.java (在network_BATNUM/)
       研究改兩檔案之差異! (fc 是 file compare工具, diff 是 Unix 系統的工具)
   (2) 研究 HaLaBat.java 如何搭配 Bat6OK.java, 並須實際測試後寫心得在報告裡;
       注意是這次給的 Bat6OK.java, 與以前給的 Bat6ok.java (小寫ok)略有差異!
       再參考 HaLaBat.java, 改為可配合你 LAB04 寫的 ID check Java application
       你可直接改 HaLa.java, 或 copy 到 別的檔名(但記得裡面也要配合改!)
   (3) 把 LAB04 的ID check程式拿來多寫一個 constructor 給你新 HaLa.java 用
       這樣就大功告成了! (參考新的 Bat6OK.java 裡面的 Constructor) 
    注意這樣你改後的 LAB04 之 ID check 程式配合 HaLa.java 就成網路版!
     不過它仍可以當作單機版獨立執行! 
    因為新寫的 Constructor 只給你新的 HaLa.java用

  參考所給範例: 
     http://www.cs.nctu.edu.tw/~tsaiwn/oop/03_Labs/Lab08/
     (a.) 人透過網路與電腦簡易對談的程式  HaLa.java
          先分開測試,  HaLa.java 執行後要從別的電腦 telnet 進去測試!
           抓 HaLa.java 
     (b.) 在 LAB04 就看過的 BATNUM 之 C 版與 Java 版
           抓 bat6ok.c 
           抓 單機版 bat6ok.java 
       注意該 bat6ok.java (小寫 ok)單機版還不能搭配HaLa.java 喔!
     (c.) 從 (b.) 之 Java 版改為網路版的 BATNUM,
          可在本目錄之下的子目錄 network_BATNUM 之下找到.
           網路版 BATNUM 

        ** 注意因為Bat6OK.java有同名檔案 bat6ok.java, 要注意自己放不同目錄! 
      (註: Unix 上大小寫不同就是不同檔案, 但 Windows 上同名但大小寫不同是沒用的!)

     * 仔細研究 network_BATNUM 下 範例, 
       研究該 Bat6OK.java 與本目錄下之前版本叫 bat6ok.java (注意大小寫)差異,
       注意名稱雖幾乎相同(Unix上算不同), 但內容有些些不同!  
       並研究 HaLaBat.java 相對於 HaLa.java 到底改了哪幾行?
          (HaLaBat.java 是用來配合 Bat6OK.java)
     * 再測試並研究 network_BATNUM 下檔案, 編譯後並測試看看!
        javac HaLaBat.java
        java HaLaBat
     * 研究如何把 HaLaBat.java 與 Bat6OK.java 結何成網路版 BATNUM?
       (各只改一點點!)
       (想不出或看不懂就找助教或同學討論:) 
     * 其實之前給大家的 bat.jar 裡面有個會先印出你的 IP 的 netBat.zip
       在演習課時助教也 Demo 過讓大家連進來玩過 ! (batnet.zip)
       這次順便再附上, 請看目錄內有個 new_netBat 子目錄, 就在裡面啦!
       (執行:   java -jar netBat.zip ) 

   *** 注意 network_BATNUM 下的 Bat6OK.java 雖與此目錄的 bat6ok.java不同,
       但 在 network_BATNUM 之下的 Bat6OK.java 仍可單獨執行!!!

You are the Website counter -th visitors to this page.
HaLa.java 簡易網路對談程式
(用 telnet 連進來就可以; 當然也可以寫一個 client 取代 telnet )

   1 //HaLa.java -- by tsaiwn@csie.nctu.edu.tw
   2 //javac HaLa.java
   3 //java HaLa
   4 //then you can use "telnet hostName 3388" to connect this program
   5 import java.io.*;
   6 import java.net.*;
   7 import java.util.*;
   8 import java.lang.*;  // NOT necessary
   9 class HaLa  {  // should be same as the File name, 建議該用大寫開頭 HaLa
  10     ServerSocket ssk;   //  用來等待連線
  11     Socket ccc;  // 有client連線進來可生出 Socket 小洞
  12     int portNumber = 3388;   // Listening TCP port
  13     PrintStream cout = System.out;    // 取個與 C++ 同名的物件變數
  14     PrintStream cerr = System.err;
  15     public static void main(String[ ]  xxx) throws Exception {
  16                                           // ^^^ 宣稱若出問題會報告馬政府 !
  17         new HaLa( );    // 要求升出 HaLa 物件並立刻執行 HaLa( ) 建構子
  18     }
  19     HaLa( ) {  // 這就是 class HaLa 的 constructor 建構子
  20        cout.println("Server started ... at port "+ portNumber);
  21        try {
  22            ssk = new ServerSocket( portNumber );
  23            while(38 == 38) { // true
  24                ccc = ssk.accept( );  // 等待連線後取得連線暫稱 ccc
  25                Brother b = new Brother(ccc); // 請一個小弟, 並把連線交給小弟 
  26                b.start( );  // 叫小弟開始處理該連線
  27            }
  28        } catch(Exception e) {
  29            cerr.println("Cannot startup ChatServer at port "+ portNumber+"!");
  30            cerr.println("Maybe port "+portNumber+" has been used?\nByebye.");
  31        }
  32   
  33     } // HaLa( )
  34 } // class HaLa
  35 ////////
  36 class Brother extends Thread {   // 表示這 class Brother 是一個 Thread 執行緒 
  37     Socket mySock;    // 用來記住"老大"傳來的資料, 方便這class內其他 function 用 
  38     Date now;
  39     private String myName;
  40     private String remoteAddr;   // 連著我的對方 IP address
  41     private BufferedReader in;
  42     private PrintWriter out;
  43     private PrintStream cout = System.out;   // cout 只是簡寫而已
  44     Brother(Socket x) { 
  45         mySock = x; 
  46         now = new Date( );
  47         try {
  48             remoteAddr = mySock.getInetAddress().getHostAddress();
  49               // 然後取得InputStream並包成 BufferedReader 方便 readLine()
  50             in = new BufferedReader(
  51                     new InputStreamReader(mySock.getInputStream()) );
  52               // 再取得 OutputStream 並包成 PrintWriter
  53             out = new PrintWriter(
  54                     new OutputStreamWriter(mySock.getOutputStream()), true );
  55             cout.println("Friend from " + remoteAddr + " at " + now);
  56         } catch(Exception e) {
  57           // say something
  58         }
  59     }
  60     public void run( ) {   // 規定 Thread 內一定要這樣寫才有用
  61            // 接著, 可以 與 連線者對話
  62         try {
  63             while(true) {
  64                out.print("Say: "); out.flush( );
  65                String s = in.readLine( );
  66                if(s.equals("/quit")) break;
  67                int k = (int) (5*Math.random( ));
  68                if(k < 1 ) out.println(" You said " + s);
  69                else if(k < 3 )out.println(" Really?");
  70                else out.println("  Ohh .. my God !");
  71             }
  72             out.println("Bye!"); 
  73             try { mySock.close( ); }catch(Exception e) {  } 
  74         }catch(Exception e) {  }
  75         cout.println("leaving ... " + remoteAddr);
  76     } // run( )
  77 } // class Brother

idcheck.c (其他請看LAB04目錄)
     1	//idcheck.c -- by tsaiwn@csie.nctu.edu.tw
     2	//gcc idcheck.c
     3	//寫個程式可以檢查身分證號碼是否正確
     4	//Extra credit: 輸入 999 則產生一個可用的合法身分證號碼
     5	////////////
     6	#include <stdio.h>
     7	#include <ctype.h>
     8	void generateID( ), squeeze(char*); // 宣告兩個 function 
     9	int testID(char*);  // 查驗身分證各種可能的錯誤 並傳回錯誤代碼
    10	void printError(int code); // print error message according code
    11	
    12	// 注意 Java 不需要宣告, 因為在 class 內沒有先後關係 
    13	
    14	int main( ) {
    15	   static char id[99];  // 夠'長吧 :-) 身分證才 10 碼啦 
    16	   int code = 0;   // 用來記住錯誤代碼 
    17	   while(38 == 38) {   // for( ;; ) {
    18	       printf("請輸入身分證號碼 Input ID: ");
    19	       fgets(id, sizeof(id), stdin); // 整列讀入 到 id
    20	       // check EOF 
    21	       if(feof(stdin)) break;  //EOF == ^D in Unix; ^Z on DOS/Window
    22	       squeeze(id); // 把所有空白都去掉  white space
    23	       //printf("strlen(id)=%d\n", strlen(id));
    24	       if(strcmp(id, "-1") == 0) break; //Java 用 id.equals("-1")
    25	       if(strcmp(id, ".") == 0) break; 
    26	       if(strcmp(id, "quit") == 0) break; 
    27	       if(id[1] == 'U') break;  // QUIT ?  // Java 用 id.charAt(1)==
    28	       if(strcmp(id, "999") == 0) {generateID( ); continue; }
    29	       code = testID(id);  // 取得錯誤代號, 0 表示無錯 :-)
    30	       printf(" ID %s is ", id);
    31	       if(code==0) printf(" OK.\n");   // 印出說這號碼正確 
    32	       else {
    33	          printf(" Error ID! Reason(s):\n");
    34	       }//if
    35	       printError(code); // 依據 code 印訊息 
    36	   }// while(
    37	   printf("\r\nThank you and bye bye!\n");
    38	   return 0;
    39	}//main(
    40	
    41	//關於  squeeze(char*) 這好用的 function, 因為 C 程式庫沒有,自己寫:
    42	//注意 NewLine 也算 white space, 所以這函數也會把尾巴的 '\n' 拿掉!
    43	// 因為 fgets 讀入的資料尾巴有 NewLine; C++ 的 getline 則沒有 NewLine!
    44	void squeeze(char*p) {    // 擠掉所有的 white space; Java 要如何做呢?
    45	   char*p2 = p;    
    46	   if(*p == 0)return;   // NULL terminated, 一開始就字串結束: 空字串
    47	   while(*p2 !=0) {  // white space 請看  K&R課本第二章與附錄 B
    48	      if(isspace(*p2)) { ++p2; continue; }  // 丟掉 white space
    49	      *p = *p2; 
    50	      p++; ++p2;   // advance one char
    51	   }//while
    52	   *p = *p2;  // 0 == '\0'   == NULL 
    53	}// squeeze(
    54	
    55	// 關於 int testID(char* id) : 依據身分證規則查看 id 傳回錯誤代碼
    56	int yy[ ]={ 10,11,12,13,14,15,16,17, 34,  //ABCDEFGH I
    57	            18,19,20,21,22,  35,   //JKLMN O
    58	            23,24,25,26,27,28,     //PQRSTU
    59	            29,32,30,31, 33 };     //VWXY Z 
    60	int checkSum(char * id){  // 幫忙算 checkSum 給 testID(id) 用 
    61	   int sum, i;    // 因編碼沒完全照字母順序, 用算的要很多 if(...
    62	   int ynum;
    63	   // 用查表法 table look up 查出字母對應的兩位數較簡單直覺 !
    64	   // 先建個表 int yy[ ] = { 10, 11, 12, 13, ...};  // 照規定 AB..
    65	   // 然後 Let i = id[0]字母減去 'A' 得到 0..25
    66	   // 再查出 yy[i] 拿來用: ynum = yy[i];  // 10..O是35..Z不是35 !!
    67	   i = id[0] - 'A';
    68	   ynum = yy[i];
    69	   sum = ynum/10 + 9* (ynum%10);  // weight 1, 9, [876543211]
    70	   for(i=1; i<=8; ++i) sum += (id[i] - '0') *(9-i);  // 87654321
    71	   sum += (id[9] - '0') ;  // *1   檢查碼 weight 也是  1
    72	   return sum;   // 我只負責算出 checksum
    73	}//checkSum(
    74	
    75	int testID(char* id) {   //傳回錯誤代碼, 可用 bitwise "&" 運算找出 
    76	   int i, ans = 0, sum=0;  // sum 用來算 weighted check sum
    77	   id[0] = toupper(id[0]); // 轉為大寫
    78	   if(!isalpha(id[0])) ans = ans + 1;  // 1 號錯  ans = ans | 1;
    79	   if(id[1] != '1'  && id[1] != '2') ans += 2; // 2 號錯 男生女生?
    80	   if(strlen(id) < 10) ans += 4;   // 太短
    81	   if(strlen(id) > 10) ans += 8;   // 太長
    82	   for(i=1; i<=9; ++i) if(!isdigit(id[i])) ans = ans | 16; // 非數字
    83	   if((ans&16) != 0) return ans;  // 有非數字不用再算 check sum 啦 
    84	   if(ans != 0) return ans;  // 有任何錯就..就不用再算 check sum 啦 
    85	   sum = checkSum(id);   // 假設沒有其他怪字就算出 check sum
    86	   if(sum%10 != 0) ans |= 32;   // 必須除以 10 除得盡才對 
    87	   return ans;
    88	}// testID(
    89	
    90	char what[ ][88]={ "對啦!!這是合法的身份證字號", //訊息0
    91	                "ㄟ..第一個字必須是字母啦!",    // 訊息1
    92	                "你是第三性嗎?",            // 訊息2
    93	              "太短了!不足碼唷!!",      // 訊息3
    94	              "怎麼會有這麼多碼!!",     // 訊息4
    95	            "打錯啦!!應該是數字喔!!",   // 訊息5     
    96	            "神秘數字算出來是錯的??"     // 訊息6
    97	                };  // do NOT forget the ";"
    98	// String what[ ] = { ... };  // in Java 
    99	void printError(int code) {  // print all errors found use bitwise and
   100	   int i, yy[ ] = {0, 1, 2, 4, 8, 16, 32, 64, 128};
   101	   if(code == 0) { printf("%s\n", what[0]); return; }
   102	   for(i=1; i <= 6; ++i)if((code&yy[i]) != 0)printf("%s\n", what[i]);
   103	}// printError(
   104	
   105	void generateID( ) {
   106	   char id[11]={ 0 };   // 會全放 0  (NULL)  ==  '\0'   == 0
   107	   int i;
   108	   id[0] = 'A' +  rand( ) % 26;  // 'A' .. 'Z'
   109	   id[1] = '1' + rand( ) % 2;   // '1' .. '2'
   110	   for(i=2; i<=8; ++i) {
   111	     // 用亂數生出 id[2] .. id[8]
   112	     id[i] = '0' + rand( )%10;   // '0' .. '9'
   113	   } //
   114	   /// id[9] 是檢查碼, 要算, 可先塞 '0' 偷叫 checkSum( )再調整 
   115	   id[9] = '0';
   116	   i = checkSum(id);   // 借用 i 來存  checkSum
   117	   i = i % 10;
   118	   if(i != 0) id[9] = '0' + (10-i);     // 更正檢查碼 
   119	   printf(" Good ID: %s\n", id);   // legal ID now
   120	}// generateID(

抓這 LAB08 全部的壓縮檔Lab08.jar (zip格式)

抓 HaLa.java 程式檔 (簡易網路心理醫生)

Reference answer to LAB04 ( idcheck.c ==> idcheck.java Java Application)

進入 網路版 BATNUM 子目錄 (進去可抓網路版 ZIP 壓縮檔)
加分題用的: 關於用 PERL 寫 CGI 的 FAQ(常問問題) ./for_Extra_credit/             回到作業目錄             回到課程目錄

You are the free hit counters visitors to this page.