为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);
}
}
}
}
}
编译和运行
-
重新编译所有文件:
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项目更加用户友好,同时保留了原有的所有功能。