为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);}}}}
}

编译和运行

  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项目更加用户友好,同时保留了原有的所有功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/94837.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/94837.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/94837.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【云原生】Docker 搭建Kafka服务两种方式实战操作详解

目录 一、前言 二、Docker 搭建kafka介绍 2.1 Docker 命令部署 2.2 使用Docker Compose 部署 2.3 使用 Docker Swarm 2.4 使用 Kubernetes 2.5 部署建议 三、Docker 搭建kafka操作方式一 3.1 前置准备 3.2 完整操作过程 3.2.1 创建docker网络 3.2.2 启动zookeeper容…

DBeaver中禁用PostgreSQL SSL的配置指南

在DBeaver中为PostgreSQL连接禁用SSL是一个常见的配置&#xff0c;特别是当你的数据库服务器未启用SSL或遇到连接问题时。我来为你详细讲解操作步骤和注意事项。 &#x1f6e0;️ DBeaver中禁用PostgreSQL SSL的配置指南 详细步骤 打开驱动设置&#xff1a;在DBeaver中创建新的…

数组去重【JavaScript】

数组去重&#xff0c;并且key和val相同的对象视为相同的&#xff0c;需要去重。主函数&#xff1a;/*** 数组去重* 两个属性相同的对象也认为是相同的* param {Array} arr* return {Array} */ function uniqueArray(arr) {const result []// outer: 标签&#xff0c;标记外层循…

基于单片机设计的智能停车系统_271

文章目录 一、前言 1.1 项目介绍 【1】项目开发背景 【2】设计实现的功能 【3】项目硬件模块组成 【4】设计意义 【5】国内外研究现状 【6】摘要 1.2 设计思路 1.3 系统功能总结 1.4 开发工具的选择 【1】设备端开发 【2】上位机开发 1.5 参考文献 1.6 系统框架图 1.7 系统原理…

for in+逻辑表达式 生成迭代对象,最后转化为列表 ——注意list是生成器转化为列表,但[生成器]得到的就是一个列表,其中包含一个生成器元素

(int(digit) ** 2 for digit in str(n))这个不是 数组(list)&#xff0c;而是一个 生成器表达式 (generator expression)。它的作用是&#xff1a;str(n) 把数字 n 转成字符串&#xff0c;例如 n 82 → "82"。for digit in str(n) 遍历字符串中的每个字符 → "…

通信算法之321:verilog中generate if 用法-综合掉无用分支

文章目录 一.示例代码 二.优缺分析 三. generate - case 一.示例代码 提示:参考 // 根据添加/补偿频偏的标志,确定使用的频偏wire signed [WIDTH-1 : 0] freq;generateif(FREQ_FLAG == 1b1) beg

Shell 入门

目录 一、Shell 是什么 二、 .sh 脚本调用 .py 脚本 Python 核心逻辑脚本&#xff08;data_processor.py&#xff09; Shell 脚本&#xff08;pipeline.sh&#xff09; 三、常见命令 四、.sh脚本 1. 简单例子 2. 进阶例子 3. 猜数字游戏 一、Shell 是什么 Shell 的本…

UNet改进(36):融合FSATFusion的医学图像分割

1. 注意力机制的理论基础 1.1 空间注意力机制 空间注意力机制模拟人类视觉系统,能够关注图像中的显著区域。其核心思想是根据特征图的空间位置生成权重图,突出重要区域并抑制无关信息。常见的实现方式是通过沿通道维度的池化操作获取空间统计信息,然后通过卷积层生成空间注…

docker安装kafka、zookeeper详细步骤

Kafka 简介 Kafka 是一个分布式流处理平台,由 LinkedIn 开发并开源,主要用于高吞吐量的实时数据管道和流处理。 核心特性 高吞吐量:支持每秒百万级消息处理,适合大数据场景。 持久化存储:消息可持久化到磁盘,并支持多副本备份。 分布式架构:支持水平扩展,通过分区(P…

Photoshop - Ps 编辑图像

使用基本图像编辑技术&#xff0c;让图像展现出最好的一面。1.修剪和拉直使用裁剪工具&#xff0c;可以修剪边缘&#xff0c;更改图像的形状和大小&#xff0c;甚至可以对图像进行拉直和弯曲的操作。2.改善亮度和色彩点击菜单栏-编辑-调整-亮度/对比度&#xff0c;调整图像的亮…

SpringBoot 配置文件在运维开发中的应用

在 SpringBoot 项目的运维开发工作里&#xff0c;配置文件相关知识是绕不开的重要内容。它关乎着项目在不同环境下的灵活部署、参数调整&#xff0c;直接影响着应用的稳定性与可维护性。接下来&#xff0c;我们就从临时属性、属性加载优先顺序、配置文件分类这几个维度&#xf…

雷卯国产化之SE3401完全替代AOS的AO3401

上海雷卯PMOS型号SE3401 pin to pin替代AOS型号AO3401&#xff0c;已经有很多客户选用 SE3401替代AO3401&#xff0c;客户可以获得更好的价格和更快的交期。特别是在一些受空间收下限的小电子设备很受青睐。参数对比如下&#xff1a;以下是SE3401&#xff08;P沟道MOSFET&#…

多语言与零样本语音识别新突破:基于发音特征分类的方法

多语言与零样本语音识别新突破:基于发音特征分类的方法 在语音识别领域,多语言和零样本(zero-shot)语言的识别一直是一个极具挑战性的课题。近期,京都大学的研究团队提出了一种全新的方法,通过引入国际音标(IPA)和发音特征(Articulatory Features)来提升语音识别系统…

1.8 Memory

1.Memory简介 2.Memory中各参数的功能和作用1.Memory简介 1).Memory(内存)模块它是unity中监视内存使用情况的关键工具, 它能帮助我们识别哪些资源在占有大量内存,是否存在内存泄露或垃圾回收频繁等问题2).游戏中如果出现以下问题, 可以重点观察memorya.游戏卡顿, 掉帧- 表现: …

国家统计局数据爬取——机器学习

这个代码是一个用于爬取国家统计局网站数据的Python脚本。下面我将详细解释代码的各个部分及其设计思路。 代码结构概述 import requests # 发送HTTP请求 from bs4 import BeautifulSoup # 解析HTML import pandas as pd # 数据处理和存储 import time # 时间控制 import…

内存管理(智能指针,内存对齐,野指针,悬空指针)

&#x1f4cc; 1. 野指针 (Wild Pointer)什么是野指针&#xff1f;野指针指的是未初始化的指针变量。它指向的内存地址是随机的、未知的。产生原因cppint* ptr; // 野指针&#xff01;未初始化&#xff0c;指向随机地址 *ptr 10; // 危险&#xff01;可能破坏系统内存char* st…

Spring代理的特点

一.Spring代理的特点1.依赖注入和初始化影响的是原始的对象。2.代理和目标是两个对象&#xff0c;二者成员变量不共用数据。二.测试首先准备以下几个类。Bean1package com.example.springdemo.demos.a13;import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.…

嵌入式学习日记(35)TCP并发服务器构建

单循环服务器&#xff1a;服务端同一时刻只能处理一个客户端的任务并发服务器&#xff1a;服务端同一时刻可以处理多个客户端的任务TCP并发服务器构建&#xff1a;TCP&#xff1a; 1. 建立连接&#xff0c;一对一TCP服务端并发模型&#xff1a;1. 多进程进程资源开销大&#x…

【Flask】测试平台开发实战-第二篇

概述&#xff1a;在上一篇我们已经将后端初始化服务后前端的vue框架初始化已经搭建完整&#xff0c;并且可以运行看到界面&#xff0c;服务正常&#xff0c;当然我们看到的前端数据都是mock下的假数据&#xff0c;并不是真实的&#xff0c;这篇我们来开发实现第一个功能&#x…

聊一聊 .NET 的 AssemblyLoadContext 可插拔程序集

一&#xff1a;背景 1. 讲故事 最近在分析一个崩溃dump时&#xff0c;发现祸首和AssemblyLoadContext有关&#xff0c;说实话这东西我也比较陌生&#xff0c;后来查了下大模型&#xff0c;它主要奔着替代 .NetFrameWork 时代的 AppDomain 的&#xff0c;都是用来做晚期加卸载&a…