【VSCode】VSCode为Java C/S项目添加图形用户界面

为Java C/S项目添加图形用户界面

现在我们来为它添加图形用户界面(GUI)。我将使用Java Swing库创建一个简单的GUI,因为它内置于Java标准库中,无需额外依赖。

客户端GUI实现

首先,我们将修改客户端代码,添加一个Swing GUI界面:

修改后的Client.java(带GUI)

java 复制代码
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;
        }
        
        // 在新线程中连接服务器,避免阻塞UI
        new 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(带管理界面)

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;
        }
        
        @Override
        public 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);
                }
            }
        }
    }
}

编译和运行

  1. 重新编译所有文件:

    复制代码
    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
  2. 先运行服务器:

    复制代码
    java -cp "server/bin;shared/bin" Server
  3. 然后运行一个或多个客户端:

    复制代码
    java -cp "client/bin;shared/bin" Client

界面功能说明

客户端界面功能:

  1. 输入用户名
  2. 连接/断开服务器按钮
  3. 消息输入框和发送按钮
  4. 聊天内容显示区域

服务器界面功能:

  1. 启动/停止服务器按钮
  2. 服务器日志显示区域
  3. 显示客户端连接状态

扩展建议

  1. 美化界面:使用更现代的界面风格,可以考虑使用JavaFX而不是Swing
  2. 添加更多功能
    • 用户列表显示
    • 私聊功能
    • 文件传输
    • 表情符号支持
  3. 持久化存储:将聊天记录保存到数据库
  4. 用户认证:添加登录/注册功能
  5. 群组聊天:支持创建和加入不同的聊天室

这个GUI版本应该能让您的C/S项目更加用户友好,同时保留了原有的所有功能。

相关推荐
渣哥5 分钟前
如果没有双亲委派,Java 会乱成什么样?
java
jokr_13 分钟前
C++ STL 顶层设计与安全:迭代器、失效与线程安全
java·c++·安全
Code_Artist18 分钟前
[Java并发编程]6.并发集合类:ConcurrentHashMap、CopyOnWriteArrayList
java·后端·源码阅读
j_xxx404_19 分钟前
数据结构:单链表的应用(力扣算法题)第一章
c语言·数据结构·算法·leetcode
爬虫程序猿21 分钟前
利用 Java 爬虫按关键字搜索 1688 商品详情 API 返回值说明实战指南
java·开发语言·爬虫
前端赵哈哈1 小时前
初学者入门:Android 实现 Tab 点击切换(TabLayout + ViewPager2)
android·java·android studio
jokr_1 小时前
C++ 指针与引用面试深度解析
java·c++·面试
杨杨杨大侠1 小时前
第6篇:链路追踪系统 - 分布式环境下的请求跟踪
java·后端·apache log4j
乘风归趣1 小时前
spire.doc在word中生成公式
java·开发语言·word
kong@react1 小时前
docker部署spring boot,安装jdk17、maven3.8.8详细步骤
java·spring boot·docker