• 【第97题】JAVA高级技术-网络编程16(简易聊天室11:实现客户端群聊)


    回城传送–》《JAVA筑基100例》

    零、前言

    ​ 今天是学习 JAVA语言 打卡的第97天,每天我会提供一篇文章供群成员阅读( 不需要订阅付钱 ),读完文章之后,按解题思路,自己再实现一遍。在小虚竹JAVA社区 中对应的 【打卡贴】打卡,今天的任务就算完成了。

    ​ 因为大家都在一起学习同一篇文章,所以有什么问题都可以在群里问,群里的小伙伴可以迅速地帮到你,一个人可以走得很快,一群人可以走得很远,有一起学习交流的战友,是多么幸运的事情。

    ​ 学完后,自己写篇学习报告的博客,可以发布到小虚竹JAVA社区 ,供学弟学妹们参考。

    ​ 我的学习策略很简单,题海策略+ 费曼学习法。如果能把这100题都认认真真自己实现一遍,那意味着 JAVA语言 已经筑基成功了。后面的进阶学习,可以继续跟着我,一起走向架构师之路。

    一、题目描述

    题目实现:不同的客户端之间需要进行通信,一个客户端与其他的多个客户端进行通信,实现群聊功能。

    实现一个客户端与其他多个客户端进行通信,运行程序,服务器启动后,启动3个客户端程序,然后通过第一个客户端向另外两个客户端发送信息,则另外的两个客户端都会收到服务器发送的信息。

    二、解题思路

    创建一个服务类:ClientOneToManyServerFrame,继承JFrame类

    定义一个createSocket()方法,用于创建服务Socket和监听客户端程序。以及创建并启动线程对象并将接收到的客户端发送的信息转发给其他客户端。

    创建一个客户端类:ClientOneToManyClientFrame,继承JFrame类

    定义一个createClientSocket()方法,用于创建与服务器连接的Socket对象,输出流对象,以及启动线程对象接收服务器端转发的信息。

    技术重点:

    在服务器端通过线程对客户端发送的信息进行监听,当有客户端发送信息时,就会将该信息发送给其他已经登录到服务器的客户端,但是不会向发送方发送该信息,在客户端也通过线程来监听服务器转发的信息。
    (1)在服务器端创建线程类ServerThread,用于接收客户端发送的信息,并转发给其他已经连接到服务器的客户端。

    (2)在客户端创建线程类ClientThread,用于接收服务器端转发的客户端信息,并在客户端的文本域中显示接收到的信息。

    启动多个客户端:

    1、把项目打成jar包:利用maven 的clean install

    如图

    会在target目录下生成jar包

    如图

    2、进入target目录,使用java -cp的命令运行指定的类

    java -cp 命令cp 指的就是classpath。使用命令可以运行jar中的某个指定的类(要包含全路径的包名)

    进入cmd命令模式

    如图

    运行服务端

    java -cp basics98-1.0-SNAPSHOT.jar com.xiaoxuzhu.ClientOneToManyServerFrame

    运行多个客户端

    java -cp basics98-1.0-SNAPSHOT.jar com.xiaoxuzhu.ClientOneToManyClientFrame

    三、代码详解

    ClientOneToManyServerFrame

    package com.xiaoxuzhu;
    import java.awt.BorderLayout;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.PrintWriter;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.Vector;
    import javax.swing.JFrame;
    import javax.swing.JScrollPane;
    import javax.swing.JTextArea;
    /**
     * Description:
     *
     * @author xiaoxuzhu
     * @version 1.0
     *
     * <pre>
     * 修改记录:
     * 修改后版本	        修改人		修改日期			修改内容
     * 2022/6/5.1	    xiaoxuzhu		2022/6/5		    Create
     * </pre>
     * @date 2022/6/5
     */
    public class ClientOneToManyServerFrame extends JFrame {
        private JTextArea ta_info;
        private ServerSocket server; // 声明ServerSocket对象
        private Socket socket; // 声明Socket对象socket
        private Vector<Socket> vector = new Vector<Socket>();// 用于存储连接到服务器的客户端套接字对象
    
        public void createSocket() {
            try {
                server = new ServerSocket(9527);
                while (true) {
                    ta_info.append("等待新客户连接......\n");
                    socket = server.accept();// 创建套接字对象
                    vector.add(socket);// 将套接字对象添加到向量对象中
                    ta_info.append("客户端连接成功。" + socket + "\n");
                    new ServerThread(socket).start();// 创建并启动线程对象
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        class ServerThread extends Thread {
            Socket socket;
            public ServerThread(Socket socket) {
                this.socket = socket;
            }
            public void run() {
                try {
                    BufferedReader in = new BufferedReader(new InputStreamReader(
                            socket.getInputStream()));// 创建输入流对象
                    while (true) {
                        String info = in.readLine();// 读取信息
                        for (Socket s : vector) {// 遍历所有客户端套接字对象
                            if (s != socket) {// 如果不是发送信息的套接字对象
                                PrintWriter out = new PrintWriter(s
                                        .getOutputStream(), true);// 创建输出流对象
                                out.println(info);// 发送信息
                                out.flush();// 刷新输出缓冲区
                            }
                        }
                    }
                } catch (IOException e) {
                    ta_info.append(socket + "已经退出。\n");
                    vector.remove(socket);// 移除退出的客户端套接字
                }
            }
        }
    
        /**
         * Launch the application
         *
         * @param args
         */
        public static void main(String args[]) {
            ClientOneToManyServerFrame frame = new ClientOneToManyServerFrame();
            frame.setVisible(true);
            frame.createSocket();
        }
    
        /**
         * Create the frame
         */
        public ClientOneToManyServerFrame() {
            super();
            setTitle("客户端一对多通信——服务器端程序");
            setBounds(100, 100, 385, 266);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            final JScrollPane scrollPane = new JScrollPane();
            getContentPane().add(scrollPane, BorderLayout.CENTER);
    
            ta_info = new JTextArea();
            scrollPane.setViewportView(ta_info);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101

    ClientOneToManyClientFrame

    package com.xiaoxuzhu;
    import java.awt.BorderLayout;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.PrintWriter;
    import java.net.Socket;
    import java.net.UnknownHostException;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.JScrollPane;
    import javax.swing.JTextArea;
    import javax.swing.JTextField;
    /**
     * Description: 
     *
     * @author xiaoxuzhu
     * @version 1.0
     *
     * <pre>
     * 修改记录:
     * 修改后版本	        修改人		修改日期			修改内容
     * 2022/6/5.1	    xiaoxuzhu		2022/6/5		    Create
     * </pre>
     * @date 2022/6/5
     */
    public class ClientOneToManyClientFrame extends JFrame{
        private JTextArea ta_info;
        private JTextField tf_send;
        PrintWriter out;// 声明输出流对象
    
        /**
         * Launch the application
         *
         * @param args
         */
        public static void main(String args[]) {
            EventQueue.invokeLater(new Runnable() {
                public void run() {
                    try {
                        ClientOneToManyClientFrame frame = new ClientOneToManyClientFrame();
                        frame.setVisible(true);
                        frame.createClientSocket();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    
        public void createClientSocket() {
            try {
                Socket socket = new Socket("127.0.0.1", 9527);// 创建套接字对象
                out = new PrintWriter(socket.getOutputStream(), true);// 创建输出流对象
                new ClientThread(socket).start();// 创建并启动线程对象
            } catch (UnknownHostException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        class ClientThread extends Thread {
            Socket socket;
    
            public ClientThread(Socket socket) {
                this.socket = socket;
            }
    
            public void run() {
                try {
                    BufferedReader in = new BufferedReader(new InputStreamReader(
                            socket.getInputStream()));// 创建输入流对象
                    while (true) {
                        String info = in.readLine();// 读取信息
                        ta_info.append(info + "\n");// 在文本域中显示信息
                        if (info.equals("88")) {
                            break;// 结束线程
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        private void send() {
            String info = tf_send.getText();// 获得输入的信息
            if (info.equals("")) {
                return;// 如果没输入信息则返回,即不发送
            }
            if (info.equals("88")) {
                System.exit(0);// 如果没输入信息是88,则退出
            }
            out.println(info);// 发送信息
            out.flush();// 刷新输出缓冲区
            tf_send.setText(null);// 清空文本框
        }
        /**
         * Create the frame
         */
        public ClientOneToManyClientFrame() {
            super();
            setTitle("客户端一对多通信——客户端程序");
            setBounds(100, 100, 385, 266);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            final JPanel panel = new JPanel();
            getContentPane().add(panel, BorderLayout.SOUTH);
    
            final JLabel label = new JLabel();
            label.setText("输入聊天内容:");
            panel.add(label);
    
            tf_send = new JTextField();
            tf_send.addActionListener(new ActionListener() {
                public void actionPerformed(final ActionEvent e) {
                    send();// 调用方法发送信息
                }
            });
            tf_send.setPreferredSize(new Dimension(180, 25));
            panel.add(tf_send);
    
            final JButton button = new JButton();
            button.addActionListener(new ActionListener() {
                public void actionPerformed(final ActionEvent e) {
                    send();// 调用方法发送信息
                }
            });
            button.setText("发  送");
            panel.add(button);
    
            final JScrollPane scrollPane = new JScrollPane();
            getContentPane().add(scrollPane, BorderLayout.CENTER);
    
            ta_info = new JTextArea();
            scrollPane.setViewportView(ta_info);
            //
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146

    服务器启动

    如图

    客户端1向其他客户端群发信息

    如图

    客户端2接收到的信息
    如图

    客户端3接收到的信息

    如图

    四、推荐专栏

    《JAVA从零到壹》

    《JAVA筑基100例》

    五、示例源码下载

    关注下面的公众号,回复筑基+题目号

    筑基97

  • 相关阅读:
    stash拯救犹豫不决的commit
    cv::solvePnP使用方法及注意点详解(OpenCV/C++)
    程序猿读历史
    OSI七层模型
    Java中的继承是什么?
    接口开发知识点整理三
    leetcode 406. 根据身高重建队列
    Java-认识类和对象
    Linux安装Nginx详细步骤
    海量物理刚体 高性能物理引擎Unity Physics和Havok Physics的简单性能对比
  • 原文地址:https://blog.csdn.net/shi_hong_fei_hei/article/details/125461979