• TCP案例-实时群聊


    实现思路

    • 服务器端循环获取所有的客户端socket,放到一个socket的list中,等到需要通信的时候,调用相对应的管道就可以了
    • 客户端和服务器端一样,纪要发送消息,也要接受消息

    代码

    客户端

    // tcp客户端
    public class Client {
        public static void main(String[] args) throws IOException {
            // 1. 创建一个Socket对象
            // 参数一:服务端的ip地址
            // 参数二:服务端的端口号
            Socket socket = new Socket("127.0.0.1", 8888);
    
            // 创建一个独立的线程负责读取服务端发送过来的数据
            ClientReaderThread clientReaderThread = new ClientReaderThread(socket);
            clientReaderThread.start();
    
            // 2. 通过Socket对象获取一个输出流
            OutputStream outputStream = socket.getOutputStream();
    
            // 3. 把低级的字节输出流包装成高级的数据输出流
            // 这一步不是必须得,但是高级的数据输出流可以更方便的操作数据
            DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
    
            Scanner scanner = new Scanner(System.in);
            while (true) {
                System.out.println("请输入要发送的数据:");
                String s = scanner.nextLine();
    
                // 4. 使用数据输出流向服务端发送数据
                dataOutputStream.writeUTF(s);
                dataOutputStream.flush();
    
                if (s.equals("exit")) {
                    System.out.println("客户端退出");
                    // 5. 关闭资源
                    // dataOutputStream关闭的时候会自动关闭outputStream
                    dataOutputStream.close();
                    socket.close();
                    Server.removeSocket(socket);
                    break;
                }
            }
        }
    }
    
    • 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

    客户端读取线程

    读取线程是子线程

    public class ClientReaderThread extends Thread {
    
        private Socket socket;
    
        public ClientReaderThread(Socket socket) {
            this.socket = socket;
        }
    
        @Override
        public void run() {
    
            try (
                    // 1. 通过Socket对象获取一个输入流
                    InputStream inputStream = socket.getInputStream();
                    // 2. 将原始的字节输入流包装成高级的数据输入流
                    // 这一步不是必须的,但是高级的数据输入流可以更方便的操作数据
                    DataInputStream dataInputStream = new DataInputStream(inputStream);
    
            ) {
                while (true) {
                    // 3. 读取客户端发送过来的数据
                    String data = dataInputStream.readUTF();
                    System.out.println("客户端发送过来的数据:" + data);
                    System.out.println("客户端的ip地址:" + socket.getInetAddress().getHostAddress()
                            + ",客户端的端口号:" + socket.getPort());
                    if (data.equals("exit")) {
                        System.out.println("客户端退出");
                        // 4. 关闭资源
                        socket.close();
                        break;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    • 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

    服务端

    // tcp服务端
    public class Server {
    
        // 用来存储所有的socket对象
        private static ArrayList<Socket> onLineSockets = new ArrayList<Socket>();
    
        public static void main(String[] args) throws IOException {
            // 1. 创建服务端对象
            // 参数:服务端的端口号
            ServerSocket server = new ServerSocket(8888);
    
    
            while(true){
                // 2. 服务端一直处于监听状态,等待客户端的连接
                // accept()方法是一个阻塞方法,会一直等待客户端的连接
                Socket socket = server.accept();
                // 将socket对象存储到集合中
                onLineSockets.add(socket);
    
                // 3. 将socket独享交给独立的线程去负责
                ServerReaderThread serverReaderThread = new ServerReaderThread(socket);
                serverReaderThread.start();
            }
        }
    
        public static void removeSocket(Socket socket){
            onLineSockets.remove(socket);
        }
    
        public static ArrayList<Socket> getOnLineSockets(){
            return onLineSockets;
        }
    }
    
    • 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

    服务段读取线程

    public class ServerReaderThread extends Thread {
    
        private Socket socket;
    
        public ServerReaderThread(Socket socket) {
            this.socket = socket;
        }
    
        @Override
        public void run() {
    
            try (
                    // 1. 通过Socket对象获取一个输入流
                    InputStream inputStream = socket.getInputStream();
                    // 2. 将原始的字节输入流包装成高级的数据输入流
                    // 这一步不是必须的,但是高级的数据输入流可以更方便的操作数据
                    DataInputStream dataInputStream = new DataInputStream(inputStream);
    
            ) {
                while (true) {
                    // 3. 读取客户端发送过来的数据
                    String data = dataInputStream.readUTF();
                    // System.out.println("客户端发送过来的数据:" + data);
                    // System.out.println("客户端的ip地址:" + socket.getInetAddress().getHostAddress()
                    //         + ",客户端的端口号:" + socket.getPort());
                    // 4. 将客户端发送过来的数据转发给所有的客户端
                    sendMsgToAllClient(data);
                    if (data.equals("exit")) {
                        System.out.println("客户端退出");
                        // 4. 关闭资源
                        socket.close();
                        break;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        // 异常抛出给上一级
        private void sendMsgToAllClient(String data) throws Exception{
            // 1. 获取所有的socket对象
            for (Socket socket : Server.getOnLineSockets()) {
                // 2. 通过socket对象获取一个输出流
                // 3. 将原始的字节输出流包装成高级的数据输出流
                // 这一步不是必须的,但是高级的数据输出流可以更方便的操作数据
                DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());
                // 4. 使用数据输出流向客户端发送数据
                dataOutputStream.writeUTF(data);
                dataOutputStream.flush();
            }
        }
    }
    
    • 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

    缺点

    • 只能实现群发,无法端到端
    • 也会发给自己
  • 相关阅读:
    升级打怪课堂的题库更新了!
    leetcode笔记(自用)
    有了这一特性,不再安装脚手架,可创建Vue或React项目
    Windows连接Samba服务时报“找不到网络名”
    Swift运行时(派发机制)
    pytest(9)-标记用例(指定执行、跳过用例、预期失败)
    记一个 Nvidia Control Panel 打不开的问题
    想学嵌入式开发,薪资怎么样?
    什么是hive的高级分组聚合,它的用法和注意事项以及性能分析
    壹基金防灾减灾宣传进社区 提升家庭安全能力
  • 原文地址:https://blog.csdn.net/qq_40603125/article/details/138122055