前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >基于NIO的多客户端群聊

基于NIO的多客户端群聊

作者头像
冷环渊
发布2021-11-18 09:46:41
2250
发布2021-11-18 09:46:41
举报
文章被收录于专栏:冷环渊的全栈工程师历程

基于NIO的多客户端群聊

分析需求

服务端
  • 监听客户端状态
  • 保存客户端聊天记录
  • 将客户端的信息分发给其他客户端 群聊
客户端
  • 连接服务端
  • 接受服务端分发的消息
  • 发出消息

代码编写

代码里有详细的注释,这里我们主要是看一下编写步骤

服务端

---------------------初始化------------------------------ 1.开启serversocket通道 2.开启选择器 3.设置非阻塞,注册任务 --------------------监听客户端------------------- 1.判断是否有连接 2.有链接打印用户上线日志 ---------------读取客户端发送到信息--------------------- 1.打开对应的通道 2.打印消息日志 3.分发消息给其他客户端

代码语言:javascript
复制
  //服务端通道
    private ServerSocketChannel channel;
    // 多路复用选择器
    private Selector selector;

    public chatServer() {
        try {
            channel = ServerSocketChannel.open();
            selector = Selector.open();
            SocketAddress address = new InetSocketAddress(6666);
            channel.socket().bind(address);
            channel.configureBlocking(false);
            channel.register(selector, SelectionKey.OP_ACCEPT);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //    监听客户端变化
    public void listenClient() throws Exception {
        System.out.println("服务端开始监听客户端变化");
        while (true) {
        //获取需要处理的事件
            int num = selector.select();
            if (num == 0) {
                continue;
            }
            Set<SelectionKey> set = selector.selectedKeys();
            Iterator<SelectionKey> iterator = set.iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove();
                if (key.isAcceptable()) {
                	//获取连接
                    SocketChannel Clientchannel = channel.accept();
                    //设置非阻塞
                    Clientchannel.configureBlocking(false);
                    //注册任务
                    Clientchannel.register(selector, SelectionKey.OP_READ);
                    //打印用户上线日志
                    System.out.println("用户:" + Clientchannel.socket().getRemoteSocketAddress() + "上线了");
                    continue;
                } else if (key.isReadable()) {
                    readData(key);
                }
            }
        }


    }
//读取信息
    public void readData(SelectionKey key) {
        SocketChannel channel = null;
        try {
            //获取当前的信道
            channel = (SocketChannel) key.channel();
            //创建缓冲区
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int read = channel.read(buffer);
            //>0代表有可读取的内容
            if (read > 0) {
                String msg = "用户" + channel.socket().getRemoteSocketAddress() + ":" + new String(buffer.array());
                //打印信息
                System.out.println(msg);
                sendToOther(msg, channel);
            }
        } catch (Exception e) {
            System.out.println("用户:" + channel.socket().getRemoteSocketAddress() + "下线了");
            key.cancel();
            try {
                channel.close();
            } catch (Exception e1) {
                e1.printStackTrace();
            }
        }
    }

//信息分发
    public void sendToOther(String msg, SocketChannel selfSocketChannel) throws Exception {
        //获得所有的信道,可以理解为获取所有的用户
        Set<SelectionKey> set = selector.keys();
        for (SelectionKey key : set) {
            Channel channel = key.channel();
            //判断这是一个用户,并且不是发信息的那个人
            //这个是一个 socketchannel 并且不等价与我们发信息的 selfsocketchannel
            if (channel instanceof SocketChannel && channel != selfSocketChannel) {
                SocketChannel socketChannel = (SocketChannel) channel;
                ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
                socketChannel.write(buffer);
            }
        }
    }
//启动客户端,等待连接
    public static void main(String[] args) throws Exception {
        chatServer chatServer = new chatServer();
        chatServer.listenClient();
    }
客户端

---------------------初始化------------------------------ 1.连接服务端通道 2.开启选择器 3.注册任务,打印上线日志 --------------------发送信息------------------- 1.向服务端发送信息 --------------------读取信息--------------------- 1.接收服务端分发的消息(自己除外)

代码语言:javascript
复制
   //服务端通道
    private SocketChannel channel;
    // 多路复用选择器
    private Selector selector;

    public chatClient() throws Exception {
        channel = SocketChannel.open();
        selector = Selector.open();
        SocketAddress address = new InetSocketAddress("127.0.0.1", 6666);
        channel.connect(address);
        channel.configureBlocking(false);
        channel.register(selector, SelectionKey.OP_READ);
        System.out.println("用户:" + channel.getLocalAddress() + "上线了");
    }

    public void sendData(String msg) {
        try {
            ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
            channel.write(buffer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void readData() throws Exception {
        SocketChannel channel = null;
        while (true) {
            int num = selector.select();
            if (num == 0) {
                continue;
            }
            Set<SelectionKey> set = selector.selectedKeys();
            Iterator<SelectionKey> iterator = set.iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove();
                if (key.isReadable()) {
                    try {
                        //获取当前的信道
                        channel = (SocketChannel) key.channel();
                        //创建缓冲区
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        int read = channel.read(buffer);
                        //>0代表有可读取的内容
                        if (read > 0) {
                            String msg = new String(buffer.array());
                            System.out.println(msg);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();

                    }
                }
            }

        }
    }

    public static void main(String[] args) throws Exception {
        final chatClient client = new chatClient();
        new Thread() {
            @Override
            public void run() {
                while (true) {

                    try {
                        //一直阅读是否有信息
                        client.readData();
                        //每一秒阅读一次
                        sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();

        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNextLine()) {
            String str = scanner.nextLine();
            client.sendData(str);
        }
    }

案例测试

只需要创建两个类,将客户端和服务端的代码放入IDE就可以启动代码了,小冷保证开箱即用哦~ 客户端想要多开的话,打开这个选项就可以开很多个客户端程序了

效果图

服务端日志

客户端看到的信息

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/11/17 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 基于NIO的多客户端群聊
    • 分析需求
      • 服务端
      • 客户端
    • 代码编写
      • 服务端
        • 客户端
      • 案例测试
        • 效果图
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档