使用NIO实现一个群组聊天功能

NIO:(No Blocking IO或者New IO),非阻塞IO,是相对于传统IO来说,其意义在于网络IO的非阻塞性。

其工作流程:

在这里插入图片描述

Buffer :数据的载体
Channel:客户端到服务端的通道,负责数据(Buffer)的传输
Selector:多路复用器,用于监听Channel的事件(可对应多个Channel)

使用NIO模拟一套客户端->服务端的群组聊天功能,具体实现:
1.Server端

    public static void chatServer() throws IOException {

        //开启一个socket channel
        ServerSocketChannel socketChannel = ServerSocketChannel.open();
        //通道绑定端口9999
        socketChannel.bind(new InetSocketAddress(9999));
        //设置当前channel为非阻塞(核心)
        socketChannel.configureBlocking(false);

        //开启一个多路复用器
        Selector selector = Selector.open();

        //当前服务端channel注册到多路复用器,监听accept事件
        socketChannel.register(selector, SelectionKey.OP_ACCEPT);

        //循环检测注册到 多路复用器 的事件
        while (selector.select() > 0) {

            //多路复用器当前所有 有事件的 选择键
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()){
                SelectionKey selectionKey = iterator.next();

                //根据选择键事件类型  Acceptable事件为新的客户端连接到当前channel
                if (selectionKey.isAcceptable()) {

                    //把新连接到当前channel的客户端channel注册到当前多路复用器, 并监听read事件
                    SocketChannel channel = socketChannel.accept();
                    channel.configureBlocking(false);
                    channel.register(selector, SelectionKey.OP_READ);
                }

                //如果当前选择键事件为read,说明客户端有数据可以读取
                if (selectionKey.isReadable()) {
                    SelectableChannel channel = selectionKey.channel();
                    if (channel instanceof SocketChannel) {
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        ((SocketChannel) channel).read(buffer);

                        buffer.flip();

                        //将接受到的消息转发给所有客户端(除当前消息的发送端)
                        //这里要使用 selector.keys() 获取当前多路复用器注册的所有channel
                        for (SelectionKey key : selector.keys()) {
                            if (!key.equals(selectionKey) && key.channel() instanceof SocketChannel) {
                                SelectableChannel channel1 = key.channel();

                                //转发buffer之前先mark,便于发送完后reset到开始位置
                                //否则会导致 buf只能被发送一次
                                buffer.mark();
                                ((SocketChannel) channel1).write(buffer);
                                buffer.reset();
                            }
                        }
                        buffer.clear();

                    }
                }

                //已经处理过的选择键要及时移除,否则会导致多次重复处理导致异常
                selectionKeys.remove(selectionKey);
            }
            
        }


    }

2.Client端

public static void chatClient() throws IOException {
        //创建连接通道
        SocketChannel channel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9999));
        //设置通道为非阻塞
        channel.configureBlocking(false);

        ByteBuffer buffer = ByteBuffer.allocate(1024);
        Scanner scanner = new Scanner(System.in);
        
        //新开一个线程用于接收别的客户端发送的消息  实现群聊功能
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    ByteBuffer recBuf = ByteBuffer.allocate(1024);
                    int read = 0;
                    try {
                        read = channel.read(recBuf);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    if (read > 0) {
                        recBuf.flip();
                        byte[] bytes = new byte[recBuf.limit()];
                        recBuf.get(bytes);
                        recBuf.clear();
                        System.out.println(new String(bytes));
                    }
                }
            }
        }).start();

        while (true){
            String s = scanner.nextLine();
            if (s.equalsIgnoreCase("exit")){
                channel.close();
                break;
            }else {

                String send = "user" + index + ":" + s;
                buffer.put(send.getBytes());
                buffer.flip();
                channel.write(buffer);
                buffer.clear();
            }
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值