本來這次作業我是想搞個圖形界面的,然而現實情況是我把題意理解錯了,於是乎失去了最初的興致,還是把程序變成了功能正確但是“UI”不友好的console了,但是不管怎么樣,前期的圖形界面的開發還是很有收獲的,畢竟講真,想要把Java搞得有形有色的也是很不容易的,借助可視化的插件windowsBuilder,這個過程還是既exciting 又tiring的。
好吧 ,然而圖形界面已經成為了歷史,現在來說說這個功能正確的console 吧
我也是剛知道的Eclipse里面是可以跑好多個程序的,只要你的一個.java文件中有public static void main,他就能給你一個窗口,讓你跑起來。只不過這些窗口堆疊在一起,需要自行選擇不同的窗口進行IO操作。
總的思路是采用c/s的方式,client借助socket完成向server的發送和接受兩個工作,當然了,為了體現出真實情況下的雙工的特點,發送和接受是需要開兩個線程的,也就是說,一個用戶需要自己管理兩個線程。server則相對來講比較復雜,因為這里面涉及到了調度,server需要有發送消息給在線client的線程(這個線程要做的事情就是只要有消息就要把消息發到所有的用戶的窗口),以及接受client發來的消息的線程(這個線程要做的事情就是將接收到的消息全部交給發送消息的線程,於是這兩個線程之間的通信問題也是實現上的一個關鍵~),為了使得所有的用戶消息是同步的,server需要管理一個用戶線程的列表,用以實現用戶的行為的控制,於是乎這就要求只要有用戶請求連接服務器,服務器就要為用戶新建一個線程,那么client 和server 之間靠什么來進行聯系呢,那就是我們的socket了。

1 import java.io.BufferedReader; 2 import java.io.InputStreamReader; 3 import java.io.PrintWriter; 4 import java.net.Socket; 5 6 public class Client extends Thread{ 7 8 private static String serverIp = "127.0.0.1"; 9 private static int serverPort = 8001; 10 11 private Socket clientsSocket; //plays key role 12 private PrintWriter pw; // for send data 13 private BufferedReader br; // for receive data 14 15 public Client(){ 16 try { 17 clientsSocket = new Socket(serverIp, serverPort); 18 pw = new PrintWriter(clientsSocket.getOutputStream(),true); 19 br = new BufferedReader(new InputStreamReader(clientsSocket.getInputStream())); 20 new readServer(); 21 22 while(true){ 23 br = new BufferedReader(new InputStreamReader(System.in)); 24 String input = br.readLine(); 25 //System.out.println("this is the input :" + input); 26 pw.println(input); 27 } 28 } catch (Exception e) { 29 e.printStackTrace(); 30 } 31 } 32 33 class readServer extends Thread{ 34 private BufferedReader reader; 35 36 public readServer(){ 37 try { 38 reader = new BufferedReader(new InputStreamReader(clientsSocket.getInputStream())); 39 start(); 40 } catch (Exception e) { 41 e.printStackTrace(); 42 } 43 } 44 45 public void run(){ 46 try { 47 while(true){ 48 String content = reader.readLine(); 49 if(content.equals("bye Client")){ 50 break; 51 } 52 else { 53 System.out.println(content); 54 } 55 } 56 } catch (Exception e) { 57 e.printStackTrace(); 58 } 59 } 60 } 61 62 public static void main(String[] args) throws Exception{ 63 new Client(); 64 } 65 }

1 import java.io.BufferedReader; 2 import java.io.InputStreamReader; 3 import java.io.PrintWriter; 4 import java.net.ServerSocket; 5 import java.net.Socket; 6 import java.util.ArrayList; 7 import java.util.LinkedList; 8 9 10 public class Server { 11 12 private static int port = 8001; 13 private static boolean prit = false; 14 private static ArrayList<String> userList = new ArrayList<>(); 15 private static LinkedList<String> messageList = new LinkedList<>(); 16 private static ArrayList<ServerThread> threadsList = new ArrayList<>(); 17 18 private ServerSocket serverSocket; 19 20 public Server(){ 21 try { 22 serverSocket = new ServerSocket(port); 23 new PrintClient(); 24 25 while(true){ 26 Socket socket = serverSocket.accept(); 27 new ServerThread(socket); 28 } 29 } catch (Exception e) { 30 e.printStackTrace(); 31 } 32 } 33 34 class PrintClient extends Thread{ 35 public PrintClient(){ 36 start(); 37 } 38 39 public void run(){ 40 while(true){ 41 try { 42 Thread.sleep(10); 43 } catch (Exception e) { 44 e.printStackTrace(); 45 } 46 if (prit == true){ 47 String msg = messageList.getFirst(); 48 //System.out.println("prepare to sent to Clent"); 49 for (ServerThread sThread : threadsList){ 50 sThread.sendMessage(msg); 51 } 52 synchronized (messageList) { 53 messageList.removeFirst(); 54 } 55 prit = messageList.size() > 0 ? true : false; 56 } 57 } 58 } 59 } 60 61 class ServerThread extends Thread{ 62 private Socket client; 63 private PrintWriter pw; 64 private BufferedReader br; 65 private String user; 66 67 public ServerThread(Socket socket){ 68 try { 69 client = socket; 70 pw = new PrintWriter(client.getOutputStream(),true); 71 br = new BufferedReader(new InputStreamReader(client.getInputStream())); 72 br.readLine(); 73 74 pw.println("connect success input your name ~"); 75 start(); 76 } catch (Exception e) { 77 e.printStackTrace(); 78 } 79 } 80 81 public void pushMessage(String msg){ 82 synchronized (messageList) { 83 messageList.add(msg); 84 } 85 prit = true; 86 } 87 88 public void sendMessage(String msg){ 89 pw.println(msg); 90 } 91 92 93 public void run(){ 94 try { 95 int first = 1; 96 String msg = br.readLine(); 97 while(!msg.equals("bye")){ 98 if (first == 1){ 99 user = msg; 100 userList.add(user); 101 threadsList.add(this); 102 pw.println(user + " hello you can chat now "); 103 this.pushMessage("Client <" + user + "> " + "join in ~"); 104 //System.out.println("the prit is " + prit); 105 first--; 106 } 107 else { 108 this.pushMessage("Client<" + user + "> "+ "say :" + msg); 109 //System.out.println("the prit is " + prit + " " + messageList.size()); 110 } 111 msg = br.readLine(); 112 //System.out.println(msg); 113 } 114 pw.println("bye Client"); 115 } catch (Exception e) { 116 e.printStackTrace(); 117 }finally{ 118 try { 119 client.close(); 120 } catch (Exception e2) { 121 e2.printStackTrace(); 122 } 123 threadsList.remove(this); 124 userList.remove(user); 125 pushMessage(user + " leave ~"); 126 } 127 } 128 } 129 130 public static void main(String[] srgs) throws Exception{ 131 new Server(); 132 } 133 }
在這里注意一個問題,server端的PrintClient線程在run的過程中一定要有sleep的過程,否則會因為一開始的prit設置為false而在一直在while(true)的死循環中得不到更新,也就是說線程的同步出了錯,從而會出現客戶端無法收到信息的錯誤。還有一點要注意的是作為良好的編程習慣需要注意多個線程公用的變量要注意互斥操作,防止出現多線程中的“magic bugs”。同步與互斥,真是線程進程調度中的老大難啊,每次編程都要小心處理這兩個問題,盡量避免不必要的錯誤。