01基於BIO的多人聊天室
阿新 • • 發佈:2021-06-30
聊天室服務端實現
聊天主幹邏輯
1 package server; 2 3 import java.io.BufferedWriter; 4 import java.io.IOException; 5 import java.io.OutputStreamWriter; 6 import java.io.Writer; 7 import java.net.ServerSocket; 8 import java.net.Socket; 9 import java.util.HashMap; 10 import java.util.Map; 11 12 public classChatServer { 13 14 private int DEFAULT_PORT = 8888; 15 private final String QUIT = "quit"; 16 17 private ServerSocket serverSocket; 18 private Map<Integer, Writer> connectedClients; 19 20 public ChatServer() { 21 connectedClients = new HashMap<>(); 22 }23 24 public synchronized void addClient(Socket socket) throws IOException { 25 if (socket != null) { 26 int port = socket.getPort(); 27 BufferedWriter writer = new BufferedWriter( 28 new OutputStreamWriter(socket.getOutputStream()) 29 );30 connectedClients.put(port, writer); 31 System.out.println("客戶端[" + port + "]已連線到伺服器"); 32 } 33 } 34 35 public synchronized void removeClient(Socket socket) throws IOException { 36 if (socket != null) { 37 int port = socket.getPort(); 38 if (connectedClients.containsKey(port)) { 39 connectedClients.get(port).close(); 40 } 41 connectedClients.remove(port); 42 System.out.println("客戶端[" + port + "]已斷開連線"); 43 } 44 } 45 46 public synchronized void forwardMessage(Socket socket, String fwdMsg) throws IOException { 47 for (Integer id : connectedClients.keySet()) { 48 if (!id.equals(socket.getPort())) { 49 Writer writer = connectedClients.get(id); 50 writer.write(fwdMsg); 51 writer.flush(); 52 } 53 } 54 } 55 56 public boolean readyToQuit(String msg) { 57 return QUIT.equals(msg); 58 } 59 60 public synchronized void close() { 61 if (serverSocket != null) { 62 try { 63 serverSocket.close(); 64 System.out.println("關閉serverSocket"); 65 } catch (IOException e) { 66 e.printStackTrace(); 67 } 68 } 69 } 70 71 public void start() { 72 73 try { 74 // 繫結監聽埠 75 serverSocket = new ServerSocket(DEFAULT_PORT); 76 System.out.println("啟動伺服器,監聽埠:" + DEFAULT_PORT + "..."); 77 78 while (true) { 79 // 等待客戶端連線 80 Socket socket = serverSocket.accept(); 81 // 建立ChatHandler執行緒 82 new Thread(new ChatHandler(this, socket)).start(); 83 } 84 85 } catch (IOException e) { 86 e.printStackTrace(); 87 } finally { 88 close(); 89 } 90 } 91 92 public static void main(String[] args) { 93 ChatServer server = new ChatServer(); 94 server.start(); 95 } 96 97 }
聊天任務
1 package server; 2 3 import java.io.*; 4 import java.net.Socket; 5 6 public class ChatHandler implements Runnable { 7 8 private ChatServer server; 9 private Socket socket; 10 11 public ChatHandler(ChatServer server, Socket socket) { 12 this.server = server; 13 this.socket = socket; 14 } 15 16 @Override 17 public void run() { 18 try { 19 // 儲存新上線使用者 20 server.addClient(socket); 21 22 // 讀取使用者傳送的訊息 23 BufferedReader reader = new BufferedReader( 24 new InputStreamReader(socket.getInputStream()) 25 ); 26 27 String msg = null; 28 while ((msg = reader.readLine()) != null) { 29 String fwdMsg = "客戶端[" + socket.getPort() + "]: " + msg + "\n"; 30 System.out.print(fwdMsg); 31 32 // 將訊息轉發給聊天室裡線上的其他使用者 33 server.forwardMessage(socket, fwdMsg); 34 35 // 檢查使用者是否準備退出 36 if (server.readyToQuit(msg)) { 37 break; 38 } 39 } 40 } catch (IOException e) { 41 e.printStackTrace(); 42 } finally { 43 try { 44 server.removeClient(socket); 45 } catch (IOException e) { 46 e.printStackTrace(); 47 } 48 } 49 } 50 }
聊天室客戶端
監聽服務端推送的訊息
1 package client; 2 3 import java.io.*; 4 import java.net.Socket; 5 6 public class ChatClient { 7 8 private final String DEFAULT_SERVER_HOST = "127.0.0.1"; 9 private final int DEFAULT_SERVER_PORT = 8888; 10 private final String QUIT = "quit"; 11 12 private Socket socket; 13 private BufferedReader reader; 14 private BufferedWriter writer; 15 16 // 傳送訊息給伺服器 17 public void send(String msg) throws IOException { 18 if (!socket.isOutputShutdown()) { 19 writer.write(msg + "\n"); 20 writer.flush(); 21 } 22 } 23 24 // 從伺服器接收訊息 25 public String receive() throws IOException { 26 String msg = null; 27 if (!socket.isInputShutdown()) { 28 msg = reader.readLine(); 29 } 30 return msg; 31 } 32 33 // 檢查使用者是否準備退出 34 public boolean readyToQuit(String msg) { 35 return QUIT.equals(msg); 36 } 37 38 public void close() { 39 if (writer != null) { 40 try { 41 System.out.println("關閉socket"); 42 writer.close(); 43 } catch (IOException e) { 44 e.printStackTrace(); 45 } 46 } 47 } 48 49 public void start() { 50 51 try { 52 // 建立socket 53 socket = new Socket(DEFAULT_SERVER_HOST, DEFAULT_SERVER_PORT); 54 55 // 建立IO流 56 reader = new BufferedReader( 57 new InputStreamReader(socket.getInputStream()) 58 ); 59 writer = new BufferedWriter( 60 new OutputStreamWriter(socket.getOutputStream()) 61 ); 62 63 // 處理使用者的輸入 64 new Thread(new UserInputHandler(this)).start(); 65 66 // 讀取伺服器轉發的訊息 67 String msg = null; 68 while ((msg = receive()) != null) { 69 System.out.println(msg); 70 } 71 } catch (IOException e) { 72 e.printStackTrace(); 73 } finally { 74 close(); 75 } 76 } 77 78 public static void main(String[] args) { 79 ChatClient chatClient = new ChatClient(); 80 chatClient.start(); 81 } 82 }
處理使用者輸入
1 package client; 2 3 import client.ChatClient; 4 import java.io.*; 5 6 public class UserInputHandler implements Runnable { 7 8 private ChatClient chatClient; 9 10 public UserInputHandler(ChatClient chatClient) { 11 this.chatClient = chatClient; 12 } 13 14 @Override 15 public void run() { 16 try { 17 // 等待使用者輸入訊息 18 BufferedReader consoleReader = 19 new BufferedReader(new InputStreamReader(System.in)); 20 while (true) { 21 String input = consoleReader.readLine(); 22 23 // 向伺服器傳送訊息 24 chatClient.send(input); 25 26 // 檢查使用者是否準備退出 27 if (chatClient.readyToQuit(input)) { 28 break; 29 } 30 } 31 } catch (IOException e) { 32 e.printStackTrace(); 33 } 34 } 35 }