【VSCode】VSCode为Java C/S项目添加图形用户界面
为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项目更加用户友好,同时保留了原有的所有功能。