基于Socket+Java Swing实现聊天室

该文章展示了如何利用Java的Socket和Swing库创建一个简单的聊天室应用。服务端通过ServerSocket监听5000端口,接收客户端连接,并将接收到的消息广播给所有在线用户。客户端则负责显示聊天历史、接收消息和发送用户输入。整个系统支持多用户同时在线聊天,并能显示在线用户列表。
摘要由CSDN通过智能技术生成

基于Socket+Java Swing实现聊天室

服务端

首先要实现群聊,需要先实现一个服务端用于接收客户端发送的消息,再群发给所有客户端,所以我们先讲解服务端如何实现。

public class ChatServer {
    private List<ClientHandler> clients = new ArrayList<>();// 创建一个记录在线客户端的列表

    public static void main(String[] args) {
        System.setProperty("file.encoding", "UTF-8");//设置CMD运行的编码
        new ChatServer().start();//启动服务
    }

    public void start() {
        try (ServerSocket serverSocket = new ServerSocket(5000)) {
            System.out.println("服务器已启动,端口为: 5000,IP地址为:"+ InetAddress.getLocalHost().getHostAddress()+"(客户端请填写该IP地址或通过命令ipconfig查看本机IP地址)");

            while (true) {	//循环等待接受客户端连接,添加进列表中
                Socket socket = serverSocket.accept();
                ClientHandler clientHandler = new ClientHandler(socket);
                clients.add(clientHandler);
                clientHandler.start();
            }
        } catch (IOException ex) {
            System.out.println("错误: " + ex.getMessage());
        }
    }

    public void broadcast(String message, ClientHandler sender) {// 将消息广播至所有客户端
        for (ClientHandler client : clients) {
            if (client != sender) {// 判断如果是发送者则不发送
                client.sendMessage(message);
            }
        }
    }
    public void broadcastOnlineUsers(String status,List<String> userList, ClientHandler sender) {// 广播在线用户列表
        for (ClientHandler client : clients) {
            client.sendMessage(status+"::"+userList.toString());
        }
    }

    public void removeClient(ClientHandler clientHandler) {
        clients.remove(clientHandler);// 移除客户端
    }

    class ClientHandler extends Thread {
        private Socket socket;
        private String userName;
        private BufferedReader reader;
        private PrintWriter writer;

        public String getUserName() {
            return userName;
        }

        public void setUserName(String userName) {
            this.userName = userName;
        }

        @Override
        public String toString() {
            return "ClientHandler{" +
                    "socket=" + socket +
                    ", userName='" + userName + '\'' +
                    ", reader=" + reader +
                    ", writer=" + writer +
                    '}';
        }

        public ClientHandler(Socket socket) {
            this.socket = socket;
        }

        public void run() {
            try {
                reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
                userName = reader.readLine();
                writer.println("["+LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm")) + "]  " +  userName + " 加入聊天室!");
                writer.println("欢迎加入聊天室,当前在线人数:" + clients.size());
                broadcast(userName + " 加入聊天室!", this);
                List<String> list = clients.stream().map(l -> l.getUserName()).collect(Collectors.toList());
                broadcastOnlineUsers("JOIN",list, this);
                System.out.println("["+LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm")) + "]  " + userName + " 加入聊天室!");

                // Start reading messages from the client
                String message;
                while ((message = reader.readLine()) != null) {
                    broadcast("["+LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm")) + "]  " + userName + ": " + message, this);
                    System.out.println("["+LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm")) + "]  " + userName + ": " + message);
                }

            } catch (IOException ex) {
                System.out.println("Error: " + ex.getMessage());
            } finally {
                try {
                    socket.close();
                    removeClient(this);
                    broadcast("["+LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm")) + "]  " + userName + " 已离开.当前聊天室在线人数:" + clients.size(), this);
                    List<String> list = clients.stream().map(l -> l.getUserName()).collect(Collectors.toList());
                    broadcastOnlineUsers("QUIT",list, this);
                    System.out.println(userName + " 离开聊天室!");
                } catch (IOException ex) {
                    System.out.println("错误: " + ex.getMessage());
                }
            }
        }

        public void sendMessage(String message) {
            writer.println(message);
        }
    }
}

首先启动Socket服务,然后循环等待客户端的连接,在客户端发送消息时,进行广播该消息,当有新的客户端连接时,则广播一次在线用户列表实时更新,有客户端退出时同理。
服务端启动控制台
当有客户端连接时会同步输出日志
在这里插入图片描述

客户端

客户端是聊天时使用的,通过客户端连接服务端进行在线聊天,直接看代码

import javax.swing.*;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;

public class ChatClient extends JFrame {
    private JTextArea messageArea;
    private JTextField inputField;
    private JButton sendButton;
    private String userName;
    private Socket socket;
    private BufferedReader in;
    private PrintWriter out;
    private DefaultListModel<String> userListModel;
    private JList<String> userList;
    private JScrollPane userListScrollPane;
    JScrollPane scrollPane;

	// 构造函数设计UI
    public ChatClient(String title) {
        super(title);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setLayout(new BorderLayout());

        messageArea = new JTextArea();
        messageArea.setEditable(false);
        JPanel chatPanel = new JPanel(new BorderLayout());
        chatPanel.add(messageArea, BorderLayout.CENTER);
        chatPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
        scrollPane = new JScrollPane(chatPanel);
        scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
        scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        // 设置聊天框的背景和字体颜色
        chatPanel.setBackground(Color.WHITE);
        chatPanel.setForeground(Color.BLACK);
        add(scrollPane, BorderLayout.CENTER);

        // 定义颜色和字体样式
        SimpleAttributeSet selfStyle = new SimpleAttributeSet();
        StyleConstants.setForeground(selfStyle, Color.BLUE);
        StyleConstants.setBold(selfStyle, true);

        JPanel bottomPanel = new JPanel();
        bottomPanel.setLayout(new BorderLayout());
        inputField = new JTextField();
        sendButton = new JButton("发送");
        sendButton.setEnabled(false);
        bottomPanel.add(inputField, BorderLayout.CENTER);
        bottomPanel.add(sendButton, BorderLayout.EAST);
        add(bottomPanel, BorderLayout.SOUTH);

        userListModel = new DefaultListModel<String>();
        userList = new JList<String>(userListModel);
        userListScrollPane = new JScrollPane(userList);
        // 将userScrollPane和标题放置在userPanel中
        JPanel userPanel = new JPanel(new BorderLayout());
        JLabel userListTitleLabel = new JLabel("在线用户列表");
        JPanel userListPanel = new JPanel(new BorderLayout());
        userListPanel.add(userListTitleLabel, BorderLayout.NORTH);
        userListPanel.add(userListScrollPane, BorderLayout.CENTER);
        userPanel.add(userListPanel, BorderLayout.CENTER);
        // Set the dimensions of the list
        Dimension userListScrollPaneDimension = new Dimension(200, 400);
        userListScrollPane.setPreferredSize(userListScrollPaneDimension);
        userListScrollPane.setMinimumSize(userListScrollPaneDimension);
        // Add the user list to the GUI
        add(userPanel, BorderLayout.EAST);

        inputField.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_ENTER && sendButton.isEnabled()) {
                    sendMessage();
                }
            }
        });
        sendButton.addActionListener(e -> sendMessage());

        String serverAddress = JOptionPane.showInputDialog(this, "请输入服务器地址:", "127.0.0.1");//方便测试直接设置默认值
        String userName = JOptionPane.showInputDialog(this, "请输入用户名:", "用户" + (int) (Math.random() * 1000));// 自动生成名称
        connect(serverAddress, userName);
    }

    private void connect(String serverAddress, String userName) {
        try {
            socket = new Socket(serverAddress, 5000);
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
            out.println(userName);
            this.userName = userName;
            setTitle("聊天室 - " + userName);
            sendButton.setEnabled(true);
            inputField.requestFocus();
            new Thread(this::receiveMessage).start();
        } catch (IOException e) {
            JOptionPane.showMessageDialog(this, "连接服务器失败!");
            System.exit(1);
        }
    }

    private void sendMessage() {
        String message = inputField.getText().trim();
        if (!message.isEmpty()) {
            out.println(message);
            messageArea.append("["+LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm")) + "]  " + userName + ":" + message + "\n");
            inputField.setText("");

        }
        inputField.requestFocus();
        // 获取聊天框对应的垂直滚动条
        JScrollBar verticalScrollBar = scrollPane.getVerticalScrollBar();
        // 将滚动条滚动到最底部
        verticalScrollBar.setValue(verticalScrollBar.getMaximum());
    }

    private void receiveMessage() {
        try {
            while (true) {
                String message = in.readLine();
                if (message.startsWith("JOIN::")) {
                    userListModel.clear();
                    message = message.replace("JOIN::", "");
                    ArrayList<String> arrayList = new ArrayList<>(Arrays.asList(message.substring(1, message.length() - 1).split(", ")));
                    for (String user : arrayList) {
                        if (userName.equals(user)) {
                            userListModel.addElement(user + "(我)");
                        } else {
                            userListModel.addElement(user);
                        }
                    }
                } else if (message.startsWith("QUIT::")) {
                    userListModel.clear();
                    message = message.replace("QUIT::", "");
                    ArrayList<String> arrayList = new ArrayList<>(Arrays.asList(message.substring(1, message.length() - 1).split(", ")));
                    for (String user : arrayList) {
                        userListModel.addElement(user);
                    }
                } else {
                    messageArea.append(message + "\n");
                    // 获取聊天框对应的垂直滚动条
                    JScrollBar verticalScrollBar = scrollPane.getVerticalScrollBar();
                    // 将滚动条滚动到最底部
                    verticalScrollBar.setValue(verticalScrollBar.getMaximum());
                }
            }
        } catch (IOException e) {
            JOptionPane.showMessageDialog(this, "与服务器连接断开!");
            System.exit(1);
        }
    }

    public static void main(String[] args) {
        System.setProperty("file.encoding", "UTF-8");
        ChatClient client = new ChatClient("聊天室");
        client.setSize(600, 400);
        client.setLocationRelativeTo(null);
        client.setVisible(true);
    }
}

启动客户端界面展示

输入IP地址

在这里插入图片描述

输入用户名

在这里插入图片描述

聊天室界面

在这里插入图片描述

加入聊天室并发送消息展示

在这里插入图片描述

完整打包程序直接可以使用的下载链接

跳转链接

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值