架構設計
- Server:監聽端口,接受客戶端連線,為每個連線建立獨立執行緒處理收發訊息,維護線程安全的客戶端列表用於廣播。
- Client:連線伺服器,啟動兩條執行緒:一條負責從控制台讀取輸入並發送,一條負責接收伺服器廣播訊息並列印。
Server 端核心代碼
public class TCPServer {
private static final int PORT = 8888;
private final ServerSocket serverSocket;
private final List clients = new CopyOnWriteArrayList<>(); // 線程安全列表
private final ExecutorService executor = Executors.newCachedThreadPool();
public TCPServer() throws IOException {
serverSocket = new ServerSocket(PORT);
System.out.println("伺服器啟動,監聽端口:" + PORT);
while (true) {
Socket client = serverSocket.accept();
clients.add(client);
executor.execute(new ClientHandler(client));
}
}
private class ClientHandler implements Runnable {
private final Socket socket;
private final BufferedReader in;
private final PrintWriter out;
ClientHandler(Socket socket) throws IOException {
this.socket = socket;
this.in = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
this.out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"), true);
broadcast("用戶 [" + socket.getInetAddress() + "] 進入聊天室,當前人數:" + clients.size());
}
public void run() {
try {
String msg;
while ((msg = in.readLine()) != null) {
if ("exit".equalsIgnoreCase(msg.trim())) break;
broadcast(socket.getInetAddress() + ": " + msg);
}
} catch (IOException e) {
// 忽略連線中斷異常
} finally {
clients.remove(socket);
closeQuietly(socket, in, out);
broadcast("用戶 [" + socket.getInetAddress() + "] 離開,當前人數:" + clients.size());
}
}
private void broadcast(String msg) {
for (Socket client : clients) {
try {
new PrintWriter(new OutputStreamWriter(client.getOutputStream(), "UTF-8"), true).println(msg);
} catch (IOException ignored) {}
}
}
}
}
Client 端核心代碼
public class TCPClient {
private static final String HOST = "127.0.0.1";
private static final int PORT = 8888;
public static void main(String[] args) throws IOException {
Socket socket = new Socket(HOST, PORT);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"), true);
// 接收訊息執行緒
new Thread(() -> {
try {
String msg;
while ((msg = in.readLine()) != null) System.out.println(msg);
} catch (IOException ignored) {}
}).start();
// 發送訊息主迴圈
BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
String input;
while ((input = console.readLine()) != null) {
out.println(input);
if ("exit".equalsIgnoreCase(input.trim())) break;
}
socket.close();
}
}
關鍵改進點
- 使用
CopyOnWriteArrayList 替代 ArrayList + 同步,簡化併發控制。
- 統一採用 UTF-8 編碼,避免中文亂碼。
- 資源關閉採用 try-with-resources 或輔助方法,防止洩漏。
- 執行緒池替代手動建立執行緒,提升擴展性。