【NIO与Netty】Java NIO 群聊demo

一、效果展示

  • 服务器

在这里插入图片描述

  • 客户端A

在这里插入图片描述

  • 客户端B

在这里插入图片描述

二、服务端代码

public class ChatServer {
    public static void main(String[] args) {
        try {
            //启动服务器
            new ChatServer().startServer();
        } catch (IOException e) {
            e.printStackTrace();
            //向客户端广播服务器宕机
        }
    }

    //启动服务器
    private void startServer() throws IOException {
        //1.创建Selector
        Selector selector = Selector.open();

        //2.创建ServerSocketChannel
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        serverChannel.bind(new InetSocketAddress(8080));

        //3.设置ServerSocketChannel非阻塞模式
        serverChannel.configureBlocking(false);

        //4.将ServerSocketChannel注册监听事件:有连接接入
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("服务器启动成功!");
        //5.查询selector的选择键
        for (;;){
            int select = selector.select();
            if (select<=0){
                continue;
            }
            //5.1 循环遍历选择键,根据对应事件进行相关处理
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while(iterator.hasNext()){
                SelectionKey selectionKey = iterator.next();
                iterator.remove();
                if (selectionKey.isAcceptable()){
                    //处理连接接入
                    handleAccept(serverChannel,selector);
                }
                if (selectionKey.isReadable()){
                    //处理信息接收
                    handleRead(selectionKey,selector);
                }
            }

        }
    }

    //处理信息接收
    private void handleRead(SelectionKey selectionKey, Selector selector) throws IOException {
        //1.获取连接
        SocketChannel channel = (SocketChannel) selectionKey.channel();
        //2.读取信息
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        String message="";
        int length;
        if ((length=channel.read(buffer))!=-1){
            buffer.flip();
            message=new String(buffer.array(),0,length);
            buffer.clear();
        }
        //3.判断客户端是否退出聊天室
        if ("exit".equalsIgnoreCase(message)){
            channel.close();
        }
        channel.register(selector,SelectionKey.OP_READ);
        if (message.length()>0 && !"exit".equalsIgnoreCase(message)){
            System.out.println(message);
            //4.广播信息
            radioMessage(message,selector,channel);
        }
    }

    //广播信息
    private void radioMessage(String message, Selector selector, SocketChannel channel) throws IOException {
        //1.获取所有已经接入 channel
        Set<SelectionKey> selectionKeys = selector.keys();
        Iterator<SelectionKey> iterator = selectionKeys.iterator();
        while (iterator.hasNext()){
            SelectionKey next = iterator.next();
            //1.获取连接
            Channel channel1 = next.channel();
            //2.判断并给其他所有人广播信息
            if(channel1 instanceof SocketChannel && channel1!=channel){
                //2.1 发送信息
                ((SocketChannel)channel1).write(Charset.forName("UTF-8").encode(message));
            }
        }
    }

    //处理连接接入
    private void handleAccept(ServerSocketChannel serverChannel, Selector selector) throws IOException {
        //1.获取连接
        SocketChannel accept = serverChannel.accept();
        accept.configureBlocking(false);
        //2.注册为可读连接
        accept.register(selector,SelectionKey.OP_READ);
        //3.发送欢迎信息
        accept.write(Charset.forName("UTF-8").encode("欢迎进入聊天室!"));
    }

}

三、客户端代码

启动类

public class ChatClient {
    //启动客户端
    public static void startClient() throws IOException {
        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
        System.out.println("请输入昵称:");
        String name=br.readLine();
        //1.创建连接
        SocketChannel channel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8080));
        channel.configureBlocking(false);
        //2.创建选择器并注册
        Selector selector = Selector.open();
        channel.register(selector, SelectionKey.OP_READ);
        //3.创建线程实现异步获取消息
        new Thread(new AsynReceive(selector)).start();
        //4.获取控制台输入并发送
        String message;
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        System.out.println("输入消息(exit 退出):");
        while ((message=br.readLine())!=null && !"exit".equalsIgnoreCase(message)){
            buffer.put(name.getBytes());
            buffer.put(":".getBytes());
            buffer.put(message.getBytes());
            buffer.flip();
            channel.write(buffer);
            buffer.clear();
        }
        if ("exit".equalsIgnoreCase(message)){
            buffer.put("exit".getBytes());
            buffer.flip();
            channel.write(buffer);
            buffer.clear();
        }
        System.out.println("退出聊天室");
        channel.close();
        br.close();
    }
}

异步接收信息类

public class AsynReceive implements Runnable{
    private Selector selector;

    public AsynReceive(Selector selector) {
        this.selector = selector;
    }

    @Override
    public void run() {
        try {
            for (;;){
                int select = selector.select();
                if (select<=0){
                    continue;
                }
                //循环遍历选择键,根据对应事件进行相关处理
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while(iterator.hasNext()){
                    SelectionKey selectionKey = iterator.next();
                    if (selectionKey.isReadable()){
                        //处理信息接收
                        handleRead(selectionKey,selector);
                    }
                    iterator.remove();
                }

            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //处理信息接收
    private void handleRead(SelectionKey selectionKey, Selector selector) throws IOException {
        //1.获取连接
        SocketChannel channel = (SocketChannel) selectionKey.channel();
        //2.读取信息
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        String message="";
        int length;
        if ((length=channel.read(buffer))!=-1){
            buffer.flip();
            message=new String(buffer.array(),0,length);
            buffer.clear();
        }
        //3.判断客户端是否退出聊天室
        if ("exit".equalsIgnoreCase(message)){
            channel.close();
        }
        channel.register(selector,SelectionKey.OP_READ);
        if (message.length()>0 && !"exit".equalsIgnoreCase(message)){
            System.out.println(message);
        }
    }
}

创建客户端A

public class ClientA {
    public static void main(String[] args) {
        try {
            ChatClient.startClient();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

创建客户端B

public class ClinetB {
    public static void main(String[] args) {
        try {
            ChatClient.startClient();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

愿你满腹经纶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值