好久沒寫過這方面的東西了,為了熟悉一下基礎知識,特寫個小程式複習一下。
好多功能都是類比出來的。也沒有圖形介面,就是在視窗輸入輸出的。但功能
實現都是一樣的。
主要用了三個類來實現。
SeverToMultiClient 類負責伺服器端的控制類,主要用來接收用戶端請求然後轉寄的。
TalkServer 類負責伺服器端的處理。
TalkClient 類負責用戶端的處理。
需要先啟動 伺服器端,然後再啟動用戶端,否則會出錯。
以下為這三個類的源碼:
package TCPChat;</p><p>/**<br /> * 本Socket編程主要實現了,使用者的登入,<br /> * 使用者的添加,使用者的刪除,使用者的退出。<br /> * 以及使用者進入時的向所有用戶端的系統提示,<br />* 退出時的系統提示<br /> * 私聊等。<br /> *<br /> *<br /> * 不足地方:<br /> * 沒有控制所有的異常情況,沒有<br /> * 考慮很全,本程式只是個類比,有些地方只是<br /> * 為了實現功能,拼湊起來的。不太通用。TalkClient<br /> * 這個類太糟糕,把好多功能都放在了一起,等有時間再整理,<br /> * 沒有實現<br /> * 物件導向的思想,應該向伺服器端那樣控制邏輯和邏輯<br /> * 實現之間分開。<br /> *<br /> * 等以後有時間在整理吧。<br /> */<br />import java.net.*;<br />import java.util.ArrayList;<br />import java.io.*;</p><p>public class SeverToMultiClient<br /> extends Thread {</p><p> public static ArrayList skClientAll = new ArrayList();</p><p> public static void main(String[] args) throws IOException {<br /> ServerSocket server = new ServerSocket(5557);<br /> System.out.println("伺服器已啟動並已處於監聽狀態...");<br /> while (true) {<br /> TalkServer stmc = new TalkServer(server.accept());<br /> skClientAll.add(stmc);<br /> System.out.println("成功響應用戶端請求!");<br /> stmc.start();<br /> }<br /> }<br />}<br />
package TCPChat;</p><p>import java.net.*;<br />import java.io.*;<br />import java.text.SimpleDateFormat;<br />import java.util.Date;</p><p>public class TalkServer<br /> extends Thread {</p><p> private Socket skClient; //儲存與用戶端通訊的Socket資訊<br /> private String userName=""; //儲存用戶端使用者名稱<br /> private String tempMsg=""; //臨時存放資訊<br /> private boolean isSecret=false; //是否為私聊<br /> SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //時間格式化器<br /> private final static String WELCOMEMSG = ",已經加入到了聊天室!!!"; //歡迎語</p><p> public TalkServer(Socket c) {<br /> this.skClient = c;<br /> }</p><p> public void run() {<br /> try {<br /> //保持串連的接受流,要始終保持串連<br /> BufferedReader in = new BufferedReader(new InputStreamReader(skClient.<br /> getInputStream()));<br /> while (true) {<br /> String str = in.readLine();</p><p> //這時要刪除記錄表中的該用戶端<br /> if (str == null || "".equals(str)) {<br /> SeverToMultiClient.skClientAll.remove(this);<br /> StringBuffer tempStr = new StringBuffer();<br /> //發送到所有的用戶端<br /> for (int i = 0; i < SeverToMultiClient.skClientAll.size(); i++) {</p><p> TalkServer talkServer = (TalkServer) SeverToMultiClient.skClientAll.<br /> get(i);<br /> PrintWriter out = new PrintWriter(talkServer.skClient.<br /> getOutputStream());<br /> tempStr.append("時間 : " +<br /> sdf.format(new Date(System.currentTimeMillis())));<br /> tempStr.append(" ");<br /> tempStr.append("/""+userName+"/" 退出了該聊天室!!!");</p><p> out.println(tempStr);<br /> out.flush();<br /> tempStr.delete(0, tempStr.length());<br /> }</p><p> System.out.println("與指定客戶通訊結束!");<br /> break;<br /> }</p><p> String[] strArray = str.split("//|"); //索引0代表使用者名稱,索引1代表資訊<br /> if(strArray.length==1){<br /> tempMsg=strArray[0];<br /> }else if(strArray.length==2){<br /> tempMsg=strArray[1];<br /> }<br /> StringBuffer tempStr = new StringBuffer();<br /> System.out.println("[From Client]" + str);<br /> int tempStartUserNameIndex=tempMsg.indexOf(">>");<br /> int tempEndUserNameIndex=tempMsg.indexOf("<<");<br /> String secretMsgUserName=""; //私聊使用者名稱<br /> String secretMsg=""; //私聊資訊<br /> boolean toOther=false;<br /> boolean toMe=false;<br /> if(tempStartUserNameIndex!=-1 && tempEndUserNameIndex!=-1){<br /> secretMsgUserName=tempMsg.substring(tempMsg.indexOf(">>")+2,tempMsg.indexOf("<<")); //取得私聊的對方名字<br /> secretMsg=tempMsg.substring(tempMsg.indexOf("<<")+2); //取得私聊資訊<br /> isSecret=true;<br /> }<br /> if (tempMsg.equals(TalkClient.WELCOME)) {<br /> userName=strArray[0];<br /> }<br /> //發送到所有的用戶端<br /> for (int i = 0; i < SeverToMultiClient.skClientAll.size(); i++) {</p><p> TalkServer talkServer = (TalkServer) SeverToMultiClient.skClientAll.<br /> get(i);<br /> PrintWriter out = new PrintWriter(talkServer.skClient.getOutputStream());<br /> //如果為歡迎提示,則提醒大家,有新人加入<br /> if (tempMsg.equals(TalkClient.WELCOME)) {<br /> out.println("系統提示:" + userName + WELCOMEMSG);<br /> out.flush();<br /> continue;<br /> }<br /> if (!"".equals(secretMsgUserName) &&<br /> talkServer.userName.equals(secretMsgUserName)) { //私聊時,向對方發送私聊資訊<br /> tempStr.append("時間 : " +<br /> sdf.format(new Date(System.currentTimeMillis())));<br /> tempStr.append(" ");<br /> tempStr.append(userName + " 悄悄地告訴你說: " + secretMsg);</p><p> out.println(tempStr);<br /> out.flush();<br /> tempStr.delete(0, tempStr.length()); //迴圈一次,即還原為空白</p><p> toOther=true; //當發送過去以後,仍然置為群聊<br /> }<br /> if (!"".equals(secretMsgUserName) &&<br /> talkServer.userName.equals(userName)) { //私聊時,伺服器向自己發送私聊資訊提示<br /> tempStr.append("時間 : " +<br /> sdf.format(new Date(System.currentTimeMillis())));<br /> tempStr.append(" ");<br /> tempStr.append(userName + " 悄悄地向 "+secretMsgUserName+" 說: " + secretMsg);</p><p> out.println(tempStr);<br /> out.flush();<br /> tempStr.delete(0, tempStr.length()); //迴圈一次,即還原為空白</p><p> toMe=true; //當發送過去以後,仍然置為群聊<br /> }<br /> if(toOther && toMe){<br /> isSecret=false; //當發送過去以後,仍然置為群聊<br /> break;<br /> }<br /> if(!isSecret){ //如果是群聊時<br /> tempStr.append("時間 : " +<br /> sdf.format(new Date(System.currentTimeMillis())));<br /> tempStr.append(" ");<br /> tempStr.append(userName + " 對大家說: " + tempMsg);</p><p> out.println(tempStr);<br /> out.flush();<br /> tempStr.delete(0, tempStr.length()); //迴圈一次,即還原為空白<br /> }</p><p> }<br /> }</p><p> skClient.close();<br /> }<br /> catch (IOException ex) {<br /> }<br /> }<br />}<br />
package TCPChat;</p><p>import java.io.*;<br />import java.net.*;</p><p>public class TalkClient<br /> extends Thread {</p><p> static BufferedReader is = null;<br /> private boolean isRead=true;//邏輯掛起控制變數<br /> private String userName=""; //使用者名稱<br /> public final static String WELCOME="welcomeToYou";</p><p> public void run() {<br /> while (isRead) {<br /> try {<br /> String fromServer = is.readLine();<br /> System.out.println(fromServer);<br /> }<br /> catch (IOException e) {<br /> isRead=false; //接受資訊的邏輯掛起<br />// e.printStackTrace();<br /> }<br /> // 在系統標準輸出上列印讀入的字串</p><p> }<br /> }<br /> //獲得一個非Null 字元串<br /> private String GetAMsg(BufferedReader sin){<br /> String Msg="";<br /> try {<br /> do {<br /> ;<br /> }<br /> while ("".equals(Msg = sin.readLine())); // 從系統標準輸入讀入一字串,不可為空字串<br /> }<br /> catch (IOException ex) {<br /> }</p><p> return Msg;<br /> }<br /> public static void main(String args[]) {<br /> try {<br /> Socket socket = new Socket("127.0.0.1", 5557);<br /> // 向原生4700連接埠發出客戶請求<br /> BufferedReader sin = new BufferedReader(new InputStreamReader(<br /> System.in));<br /> // 由系統標準輸入裝置構造BufferedReader對象<br /> PrintWriter os = new PrintWriter(socket.getOutputStream());<br /> // 由Socket對象得到輸出資料流,並構造PrintWriter對象<br /> is = new BufferedReader(new InputStreamReader(socket<br /> .getInputStream()));</p><p> TalkClient talkClient=new TalkClient();<br /> talkClient.start(); //專門啟用一個線程來接受伺服器端的資訊</p><p> System.out.println("請輸入你的暱稱(匿名使用者只可以查看資訊,不可以發送資訊):");<br /> talkClient.userName = talkClient.GetAMsg(sin); // 從系統標準輸入讀入一字串,阻塞式的,不填不允許加入</p><p> os.println(talkClient.userName+"|"+WELCOME); //剛進入時,向聊天室內所有人提示,有使用者加入。用|代替,類比實現<br /> // 將從系統標準輸入讀入的字串輸出到Server<br /> os.flush();</p><p> // 由Socket對象得到輸入資料流,並構造相應的BufferedReader對象</p><p> String readline=talkClient.GetAMsg(sin);</p><p> while (!readline.equals("bye")) {<br /> // 若從標準輸入讀入的字串為 “bye”則停止迴圈<br /> os.println(readline);<br /> // 將從系統標準輸入讀入的字串輸出到Server<br /> os.flush();<br /> // 重新整理輸出資料流,使Server馬上收到該字串<br /> // 從Server讀入一字串,並列印到標準輸出上<br /> readline=talkClient.GetAMsg(sin);<br /> } // 繼續迴圈</p><p> os.close(); // 關閉Socket輸出資料流<br /> os=null;</p><p> is.close(); // 關閉Socket輸入資料流<br /> is=null;<br /> socket.close(); // 關閉Socket<br /> socket=null;<br /> }<br /> catch (Exception e) {<br /> System.out.println("Error" + e); // 出錯則列印出錯資訊<br /> }<br /> }</p><p>}<br />
如發現問題,可以聯絡我,共同交流以下。