Java Swing 实现多人聊天室系统完整代码解析
本文将深入探讨如何利用 Java Swing 图形界面库结合 Socket 网络编程技术,构建一个支持多人实时通信的聊天室系统。文章首先梳理了系统的整体模块架构,明确客户端与服务端的职责分工,随后分步提供关键类的完整源代码,包括连接管理、消息收发线程及 GUI 交互逻辑。最后演示运行流程,帮助开发者快速掌握 Java 网络应用开发的核心要点。
系统架构与模块设计
该多人聊天室系统采用经典的 C/S(客户端/服务端)架构,主要由以下三个核心模块构成:
- 服务器端 (Server):作为系统的中枢,负责监听指定端口以接收新连接,维护所有在线客户端的 Socket 对象集合,并具备将消息广播给所有已连接用户的能力。
- 客户端框架 (LinkServerFrame):用户进入系统前的入口界面,负责提供服务器 IP 地址输入框、用户名输入框以及连接按钮。主要任务是验证输入并将连接请求传递给真实的聊天界面。
- 聊天主界面 (ClientFrame) 与核心类 (Client):
Client类封装了底层的 Socket 连接、输出流和输入流操作,屏蔽了具体的 IO 细节。ClientFrame负责展示聊天记录、输入发送内容,并在后台启动独立线程持续接收来自服务器的数据,实现非阻塞式 UI 更新。
系统总体模块关系如下图所示:
核心代码实现
第 1 步:封装 Socket 通信底层逻辑
目的:创建一个通用的网络连接工具类,用于建立 TCP 连接并处理基本的字符流读写,降低业务代码的耦合度。
入口:新建 servlet.Client.java
关键参数:BufferedWriter 用于写入字符串消息,BufferedReader 用于按行读取消息。
package servlet;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;
import java.io.OutputStreamWriter;
public class Client {
Socket socket;
BufferedWriter bw;
BufferedReader br ;
public Client(String ip, int port){
try {
socket =new Socket(ip,port);
bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
}catch (UnknownHostException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}
}
public void sendMessage(String message) {
try{
bw.write(message);
bw.newLine();
bw.flush();
}catch(IOException e){
e.printStackTrace();
}
}
public String reciveMessage() {
String message=null;
try {
message = br.readLine();
}catch(IOException e) {
e.printStackTrace();
}
return message;
}
public void close(){
try{
socket.close();
}catch(IOException e){
e.printStackTrace();
}
}}第 2 步:构建聊天主界面与后台接收线程
目的:实现可视化的聊天窗口,利用 javax.swing 组件布局,并启动守护线程异步接收服务器推送的消息,防止阻塞主线程导致界面卡顿。
入口:新建 servlet.ClientFrame.java
完成状态:用户可在文本域查看历史消息,在输入框输入后点击“发送”将带有时间戳的消息推送到服务器;关闭窗口时会触发退出协议。
package servlet;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class ClientFrame extends JFrame {
private JPanel contentPane;
private JLabel lblUserName;
private JTextField tfMessage;
private JButton btnSend;
private JTextArea textArea;
private String userName;
private Client client;
public ClientFrame(String ip,String userName){
this.userName = userName;
init();//添加窗体初始化内容
addListener();
client=new Client(ip,8080);
ReadMessageThread t=new ReadMessageThread();
t.start();
}
private class ReadMessageThread extends Thread{
public void run() {
while(true) {
String str=client.reciveMessage();
textArea.append(str+"\n");//添加文本内容
}
}
}
private void init(){
setTitle("客户端");
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100,100,450,300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5,5,5,5));
contentPane.setLayout(null);
setContentPane(contentPane);
JScrollPane scrollPane = new JScrollPane();
scrollPane.setBounds(5,5,434,229);
contentPane.add(scrollPane);
textArea =new JTextArea();
scrollPane.setViewportView(textArea);
textArea .setEditable(false);
JPanel panel =new JPanel();
panel.setBounds(5,235,434,32);
contentPane.add(panel);
panel.setLayout(null);
lblUserName = new JLabel(userName);
lblUserName.setHorizontalAlignment(SwingConstants.TRAILING);
lblUserName.setBounds(2,4,55,22);
panel.add(lblUserName);
tfMessage =new JTextField();
tfMessage.setBounds(62,5,274,22);
tfMessage.setColumns(10);
panel.add(tfMessage);
btnSend =new JButton("发送");
btnSend.setBounds(336,4,93,23);
panel.add(btnSend);
tfMessage.validate();
}
private void addListener(){
btnSend.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
Date date=new Date();
SimpleDateFormat sf=new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
client.sendMessage(userName+""+sf.format(date)+":\n"+tfMessage.getText());
tfMessage.setText("")//清空
}
});
this.addWindowListener(new WindowAdapter(){//开启窗口监听
public void windowClosing(WindowEvent atg0) {
int op = JOptionPane.showConfirmDialog(ClientFrame.this,"确定要退出聊天室吗?","确定",JOptionPane.YES_NO_OPTION);
if(op == JOptionPane.YES_OPTION) {
client.sendMessage("%EXIT%:" + userName);
try {
Thread.sleep(200);
}catch(InterruptedException e) {
e.printStackTrace();
}
client.close();
System.exit(0);
}
}
});
}
}第 3 步:创建连接登录界面
目的:提供友好的登录入口,校验必填字段(IP 和用户名),成功后实例化聊天界面并隐藏当前登录窗口。
入口:新建 servlet.LinkServerFrame.java
重要参数:默认 IP 设置为 127.0.0.1 以便本地测试。
package servlet;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
@SuppressWarnings("serial")
public class LinkServerFrame extends JFrame {
private JPanel contentPane;//下方面板
private JLabel lblIP;
private JLabel lblUserName;
private JTextField tfIP;
private JTextField tfUserName;
private JButton btnLink;
public LinkServerFrame(){
setTitle("连接服务器");
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 390, 150);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5,5,5,5));
contentPane.setLayout(null);
setContentPane(contentPane);
lblIP =new JLabel("服务器IP地址:");
lblIP.setFont(new Font("微软雅黑",Font.PLAIN,14));
lblIP.setBounds(20,15,100,15);
contentPane.add(lblIP);
tfIP =new JTextField("127.0.0.1");
tfIP.setBounds(121,13,242,21);
contentPane.add(tfIP);
tfIP.setColumns(10);
lblUserName = new JLabel("用户名:");
lblUserName.setFont(new Font("微软雅黑",Font.PLAIN,14));
lblUserName.setBounds(60,40,60,15);
contentPane.add(lblUserName);
tfUserName =new JTextField();
tfUserName.setBounds(121,42,242,21);
contentPane.add(tfUserName);
tfUserName.setColumns(10);
btnLink =new JButton("连接服务器");
btnLink.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
do_btnlink_actionPerformed(e);
}
});
btnLink.setFont(new Font("微软雅黑",Font.PLAIN,14));
btnLink.setBounds(140,80,120,23);
contentPane.add(btnLink);//显示连接服务器窗口
}
public static void main(String[] args){
LinkServerFrame linkServerFrame = new LinkServerFrame();
linkServerFrame.setVisible(true);
}
protected void do_btnlink_actionPerformed(ActionEvent e) {
if(!tfIP.getText().equals("")&&!tfUserName.getText().equals("")) {//文本框的内容不能为空
dispose();//销毁当前窗口
ClientFrame clientFrame=new ClientFrame(tfIP.getText().trim(), tfUserName.getText().trim());
clientFrame.setVisible(true);//显示客户窗体
}
else {
JOptionPane.showMessageDialog(null,"文本框里的内容不能为空!","警告",JOptionPane.WARNING_MESSAGE);
}
}
}第 4 步:实现服务端广播与心跳检测
目的:服务端需持久运行监听连接,并为每个新连接分配独立线程处理消息,同时识别特殊的退出指令 %EXIT% 进行资源清理。
入口:新建 servlet.Server.java
关键逻辑:使用 HashSet<Socket> 存储活跃连接,确保广播时不遗漏任何客户端。
package servlet;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashSet;
import java.io.BufferedWriter;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class Server {
private HashSet<Socket> allSocket;
private ServerSocket server;
public Server() {
try {
server = new ServerSocket(8080);
}catch(IOException e) {
e.printStackTrace();
}
allSocket=new HashSet<Socket>();
}
public void startServer() throws IOException{
while(true) {
Socket socket =server.accept();
System.out.println("用户已经进入聊天室");
allSocket.add(socket);
ServerThread t= new ServerThread(socket);
t.start();
}
}
private class ServerThread extends Thread{
Socket socket;
public ServerThread(Socket socket) {
this.socket=socket;
}
public void run() {
BufferedReader br=null;
try {
br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
while(true) {
String str=br.readLine();
if(str.contains("%EXIT%")) {
String tmp=str.split(":")[1]+"用户退出聊天室";
sendMessageToALLClient(tmp);
allSocket.remove(socket);
socket.close();
return;
}
sendMessageToALLClient(str);
}
}catch(IOException e) {
e.printStackTrace();
}
}}
private void sendMessageToALLClient(String str) throws IOException {
for(Socket s: allSocket) {
try{
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
bw.write(str);
bw.newLine();
bw.flush();
}catch(IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Server s = new Server();
try {
s.startServer();
}catch(IOException e){
e.printStackTrace();
}
}
}运行与测试步骤
第 1 步:启动服务端程序
首先编译并运行 Server.java。程序启动后会阻塞在 accept() 方法等待客户端接入,控制台会保持静默直到有连接进来。此时可观察终端输出变化。
第 2 步:连接并进入客户端
接着运行 LinkServerFrame.java。在弹出的登录窗口中,确认 IP 为 127.0.0.1(本地测试默认值),输入唯一的用户名(如 "UserA"),点击“连接服务器”。若成功,登录窗口关闭,聊天主界面出现。
第 3 步:体验多人实时群聊
可以通过修改 main 函数中的参数或再次直接运行 LinkServerFrame 启动第二个进程模拟第二个用户(如 "UserB")。在任一客户端发送消息,其他在线客户端将立即收到包含发送者名称和时间戳的完整消息内容,实现即时通讯效果。
总结
本方案通过 Java 原生的 IO 流与多线程技术,从零构建了功能完备的多人聊天室。代码重点展示了 Socket 的复用、线程安全的集合管理以及 Swing 事件驱动模型的运用。以上就是 Java 实现多人聊天系统的全部详细内容,更多关于 Java 网络编程进阶的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









