為Java C/S項目添加圖形用戶界面
現在我們來為它添加圖形用戶界面(GUI)。我將使用Java Swing庫創建一個簡單的GUI,因為它內置于Java標準庫中,無需額外依賴。
客戶端GUI實現
首先,我們將修改客戶端代碼,添加一個Swing GUI界面:
修改后的Client.java(帶GUI)
import java.io.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;public class Client {private static final String SERVER_ADDRESS = "localhost";private static final int SERVER_PORT = 12345;// GUI組件private JFrame frame;private JTextField messageField;private JTextArea chatArea;private JButton sendButton;private JTextField nameField;private JButton connectButton;// 網絡連接private Socket socket;private ObjectOutputStream out;private ObjectInputStream in;private String clientName;private boolean connected = false;public static void main(String[] args) {SwingUtilities.invokeLater(() -> {try {new Client().createAndShowGUI();} catch (Exception e) {e.printStackTrace();}});}private void createAndShowGUI() {// 創建主窗口frame = new JFrame("聊天客戶端");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setSize(500, 400);frame.setLayout(new BorderLayout());// 頂部面板 - 連接信息JPanel topPanel = new JPanel(new FlowLayout());topPanel.add(new JLabel("用戶名:"));nameField = new JTextField("用戶", 10);topPanel.add(nameField);connectButton = new JButton("連接服務器");connectButton.addActionListener(e -> connectToServer());topPanel.add(connectButton);frame.add(topPanel, BorderLayout.NORTH);// 中間面板 - 聊天內容chatArea = new JTextArea();chatArea.setEditable(false);JScrollPane scrollPane = new JScrollPane(chatArea);frame.add(scrollPane, BorderLayout.CENTER);// 底部面板 - 消息輸入JPanel bottomPanel = new JPanel(new BorderLayout());messageField = new JTextField();messageField.addActionListener(e -> sendMessage());sendButton = new JButton("發送");sendButton.addActionListener(e -> sendMessage());bottomPanel.add(messageField, BorderLayout.CENTER);bottomPanel.add(sendButton, BorderLayout.EAST);frame.add(bottomPanel, BorderLayout.SOUTH);// 設置窗口可見frame.setVisible(true);}private void connectToServer() {if (connected) {appendToChat("已經連接到服務器");return;}clientName = nameField.getText().trim();if (clientName.isEmpty()) {JOptionPane.showMessageDialog(frame, "請輸入用戶名", "錯誤", JOptionPane.ERROR_MESSAGE);return;}// 在新線程中連接服務器,避免阻塞UInew Thread(() -> {try {socket = new Socket(SERVER_ADDRESS, SERVER_PORT);out = new ObjectOutputStream(socket.getOutputStream());in = new ObjectInputStream(socket.getInputStream());connected = true;SwingUtilities.invokeLater(() -> {connectButton.setText("斷開連接");appendToChat("已連接到服務器");});// 啟動接收消息的線程new Thread(this::receiveMessages).start();} catch (IOException e) {SwingUtilities.invokeLater(() -> {JOptionPane.showMessageDialog(frame, "無法連接到服務器: " + e.getMessage(), "連接錯誤", JOptionPane.ERROR_MESSAGE);});}}).start();}private void disconnect() {connected = false;try {if (out != null) out.close();if (in != null) in.close();if (socket != null) socket.close();} catch (IOException e) {e.printStackTrace();}SwingUtilities.invokeLater(() -> {connectButton.setText("連接服務器");appendToChat("已斷開與服務器的連接");});}private void sendMessage() {if (!connected) {JOptionPane.showMessageDialog(frame, "請先連接到服務器", "錯誤", JOptionPane.ERROR_MESSAGE);return;}String messageText = messageField.getText().trim();if (messageText.isEmpty()) {return;}try {Message message = new Message(clientName, messageText);out.writeObject(message);out.flush();messageField.setText("");} catch (IOException e) {appendToChat("發送消息失敗: " + e.getMessage());disconnect();}}private void receiveMessages() {try {Message message;while (connected && (message = (Message) in.readObject()) != null) {final Message finalMessage = message;SwingUtilities.invokeLater(() -> appendToChat(finalMessage.toString()));}} catch (IOException | ClassNotFoundException e) {if (connected) { // 只在仍然連接時顯示錯誤SwingUtilities.invokeLater(() -> {appendToChat("與服務器的連接已斷開");disconnect();});}}}private void appendToChat(String text) {chatArea.append(text + "\n");chatArea.setCaretPosition(chatArea.getDocument().getLength());}
}
服務器管理界面
雖然服務器通常不需要GUI,但為了方便監控,我們可以添加一個簡單的管理界面:
修改后的Server.java(帶管理界面)
import java.io.*;
import java.net.*;
import java.util.concurrent.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;public class Server {private static final int PORT = 12345;private static ExecutorService pool = Executors.newFixedThreadPool(10);private static ServerSocket serverSocket;private static boolean running = false;private static List<ClientHandler> clients = new ArrayList<>();// GUI組件private JFrame frame;private JTextArea logArea;private JButton startButton;private JButton stopButton;public static void main(String[] args) {SwingUtilities.invokeLater(() -> {try {new Server().createAndShowGUI();} catch (Exception e) {e.printStackTrace();}});}private void createAndShowGUI() {// 創建主窗口frame = new JFrame("聊天服務器");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setSize(600, 400);frame.setLayout(new BorderLayout());// 頂部面板 - 控制按鈕JPanel topPanel = new JPanel(new FlowLayout());startButton = new JButton("啟動服務器");startButton.addActionListener(e -> startServer());topPanel.add(startButton);stopButton = new JButton("停止服務器");stopButton.addActionListener(e -> stopServer());stopButton.setEnabled(false);topPanel.add(stopButton);frame.add(topPanel, BorderLayout.NORTH);// 中間面板 - 日志logArea = new JTextArea();logArea.setEditable(false);JScrollPane scrollPane = new JScrollPane(logArea);frame.add(scrollPane, BorderLayout.CENTER);// 底部面板 - 狀態信息JPanel bottomPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));bottomPanel.add(new JLabel("客戶端數量: 0"));frame.add(bottomPanel, BorderLayout.SOUTH);// 設置窗口可見frame.setVisible(true);}private void startServer() {if (running) {appendLog("服務器已經在運行中");return;}new Thread(() -> {try {serverSocket = new ServerSocket(PORT);running = true;SwingUtilities.invokeLater(() -> {startButton.setEnabled(false);stopButton.setEnabled(true);appendLog("服務器已啟動,監聽端口: " + PORT);});while (running) {try {Socket clientSocket = serverSocket.accept();appendLog("新客戶端連接: " + clientSocket.getInetAddress());ClientHandler clientThread = new ClientHandler(clientSocket);clients.add(clientThread);pool.execute(clientThread);} catch (IOException e) {if (running) { // 只有在服務器仍在運行時才記錄錯誤appendLog("接受客戶端連接時出錯: " + e.getMessage());}}}} catch (IOException e) {appendLog("啟動服務器失敗: " + e.getMessage());}}).start();}private void stopServer() {running = false;try {if (serverSocket != null && !serverSocket.isClosed()) {serverSocket.close();}} catch (IOException e) {appendLog("關閉服務器時出錯: " + e.getMessage());}// 斷開所有客戶端連接for (ClientHandler client : clients) {client.disconnect();}clients.clear();startButton.setEnabled(true);stopButton.setEnabled(false);appendLog("服務器已停止");}private void appendLog(String text) {SwingUtilities.invokeLater(() -> {logArea.append(new Date() + " - " + text + "\n");logArea.setCaretPosition(logArea.getDocument().getLength());});}private class ClientHandler implements Runnable {private Socket clientSocket;private ObjectOutputStream out;private ObjectInputStream in;private String clientName;public ClientHandler(Socket socket) {this.clientSocket = socket;}@Overridepublic void run() {try {out = new ObjectOutputStream(clientSocket.getOutputStream());in = new ObjectInputStream(clientSocket.getInputStream());Message message;while (running && (message = (Message) in.readObject()) != null) {clientName = message.getSender(); // 記錄客戶端名稱appendLog("收到來自 " + clientName + " 的消息: " + message.getContent());// 廣播給所有客戶端broadcastMessage(message);}} catch (IOException | ClassNotFoundException e) {appendLog("客戶端 " + (clientName != null ? clientName : clientSocket.getInetAddress()) + " 斷開連接");} finally {disconnect();clients.remove(this);}}public void disconnect() {try {if (in != null) in.close();if (out != null) out.close();if (clientSocket != null) clientSocket.close();} catch (IOException e) {// 忽略關閉時的異常}}private void broadcastMessage(Message message) {for (ClientHandler client : clients) {try {client.out.writeObject(message);client.out.flush();} catch (IOException e) {appendLog("向客戶端發送消息失敗: " + e.getMessage());client.disconnect();clients.remove(client);}}}}
}
編譯和運行
-
重新編譯所有文件:
javac -encoding UTF-8 -d shared/bin shared/src/Message.java javac -encoding UTF-8 -cp "shared/bin" -d server/bin server/src/Server.java javac -encoding UTF-8 -cp "shared/bin" -d client/bin client/src/Client.java
-
先運行服務器:
java -cp "server/bin;shared/bin" Server
-
然后運行一個或多個客戶端:
java -cp "client/bin;shared/bin" Client
界面功能說明
客戶端界面功能:
- 輸入用戶名
- 連接/斷開服務器按鈕
- 消息輸入框和發送按鈕
- 聊天內容顯示區域
服務器界面功能:
- 啟動/停止服務器按鈕
- 服務器日志顯示區域
- 顯示客戶端連接狀態
擴展建議
- 美化界面:使用更現代的界面風格,可以考慮使用JavaFX而不是Swing
- 添加更多功能:
- 用戶列表顯示
- 私聊功能
- 文件傳輸
- 表情符號支持
- 持久化存儲:將聊天記錄保存到數據庫
- 用戶認證:添加登錄/注冊功能
- 群組聊天:支持創建和加入不同的聊天室
這個GUI版本應該能讓您的C/S項目更加用戶友好,同時保留了原有的所有功能。