TCP案例-实时群聊

实现思路

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

代码

客户端

java 复制代码
// 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;
            }
        }
    }
}

客户端读取线程

读取线程是子线程

java 复制代码
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();
        }
    }
}

服务端

java 复制代码
// 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;
    }
}

服务段读取线程

java 复制代码
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();
        }
    }
}

缺点

  • 只能实现群发,无法端到端
  • 也会发给自己
相关推荐
ZSYP-S5 分钟前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
yuanbenshidiaos13 分钟前
C++----------函数的调用机制
java·c++·算法
是小崔啊31 分钟前
开源轮子 - EasyExcel01(核心api)
java·开发语言·开源·excel·阿里巴巴
黄公子学安全40 分钟前
Java的基础概念(一)
java·开发语言·python
liwulin050640 分钟前
【JAVA】Tesseract-OCR截图屏幕指定区域识别0.4.2
java·开发语言·ocr
jackiendsc1 小时前
Java的垃圾回收机制介绍、工作原理、算法及分析调优
java·开发语言·算法
Yuan_o_1 小时前
Linux 基本使用和程序部署
java·linux·运维·服务器·数据库·后端
Oneforlove_twoforjob1 小时前
【Java基础面试题027】Java的StringBuilder是怎么实现的?
java·开发语言
数据小小爬虫1 小时前
利用Java爬虫获取苏宁易购商品详情
java·开发语言·爬虫
小汤猿人类1 小时前
nacos-服务发现注册
java·开发语言·服务发现