Java NIO——通信实例:多人聊天室

基本步骤:
在这里插入图片描述

服务器

//服务器端
public class ChatServer {
    
    //服务器端启动的方法
    public void startServer() throws IOException {
        //1 创建Selector选择器
        Selector selector = Selector.open();

        //2 创建ServerSocketChannel通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

        //3 为channel通道绑定监听端口
        serverSocketChannel.bind(new InetSocketAddress(8000));
        //设置非阻塞模式
        serverSocketChannel.configureBlocking(false);

        //4 把channel通道注册到selector选择器上
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("服务器已经启动成功了");

        //5 循环,等待有新链接接入
        //while(true)
        for(;;) {
            //获取channel数量
            int readChannels = selector.select();

            if(readChannels == 0) {
                continue;
            }
            System.out.println("目前已经就绪的channel"+readChannels);
            //获取可用的channel
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            //遍历集合
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();

                //移除set集合当前selectionKey
                iterator.remove();

                //6 根据就绪状态,调用对应方法实现具体业务操作
                //6.1 如果accept状态
                if(selectionKey.isAcceptable()) {
                    acceptOperator(serverSocketChannel,selector);
                }
                //6.2 如果可读状态
                if(selectionKey.isReadable()) {
                    readOperator(selector,selectionKey);
                }
            }
        }
    }

    //处理可读状态操作
    private void readOperator(Selector selector, SelectionKey selectionKey) throws IOException {
        //1 从SelectionKey获取到已经就绪的通道
        SocketChannel socketChannel = (SocketChannel)selectionKey.channel();

        //2 创建buffer
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

        //3 循环读取客户端消息
        int readLength = socketChannel.read(byteBuffer);
        String message = "";
        if(readLength >0) {
            //切换读模式
            byteBuffer.flip();

            //读取内容
            message += Charset.forName("UTF-8").decode(byteBuffer);
        }

        //4 将channel再次注册到选择器上,监听可读状态
        socketChannel.register(selector,SelectionKey.OP_READ);

        //5 把客户端发送消息,广播到其他客户端
        if(message.length()>0) {
            //广播给其他客户端
            System.out.println(message);
            castOtherClient(message,selector,socketChannel);
        }
    }

    //广播到其他客户端
    private void castOtherClient(String message, Selector selector, SocketChannel socketChannel) throws IOException {
        //1 获取所有已经接入channel
        Set<SelectionKey> selectionKeySet = selector.keys();

        //2 循环想所有channel广播消息
        for(SelectionKey selectionKey : selectionKeySet) {
            //获取每个channel
            Channel tarChannel = selectionKey.channel();
            //不需要给自己发送
            if(tarChannel instanceof SocketChannel && tarChannel != socketChannel) {
                ((SocketChannel)tarChannel).write(Charset.forName("UTF-8").encode(message));
            }
        }
    }

    //处理接入状态操作
    private void acceptOperator(ServerSocketChannel serverSocketChannel, Selector selector) throws IOException {
        //1 接入状态,创建socketChannel
        SocketChannel socketChannel = serverSocketChannel.accept();

        //2 把socketChannel设置非阻塞模式
        socketChannel.configureBlocking(false);

        //3 把channel注册到selector选择器上,监听可读状态
        socketChannel.register(selector,SelectionKey.OP_READ);

        //4 客户端回复信息
        socketChannel.write(Charset.forName("UTF-8")
                .encode("欢迎进入聊天室,请注意隐私安全"));
    }

    //启动主方法
    public static void main(String[] args) {
        try {
            new ChatServer().startServer();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

客户端

//客户端
public class ChatClient {

    //启动客户端方法
    public void startClient(String name) throws IOException {
        //连接服务端
        SocketChannel socketChannel =
                SocketChannel.open(new InetSocketAddress("127.0.0.1", 8000));

        //接收服务端响应数据
        Selector selector = Selector.open();

        socketChannel.configureBlocking(false);
        socketChannel.register(selector, SelectionKey.OP_READ);
        //创建线程
        new Thread(new ClientThread(selector)).start();

        //向服务器端发送消息
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNextLine()) {
            String msg = scanner.nextLine();
            if (msg.length() > 0) {
                socketChannel.write(Charset.forName("UTF-8").encode(name + " : " + msg));
            }
        }
    }
}

客户端线程:

public class ClientThread implements  Runnable {

    private Selector selector;
    public ClientThread(Selector selector) {
        this.selector = selector;
    }

    @Override
    public void run() {
        try {
            //while(true)
            for(;;) {
                //获取channel数量
                int readChannels = selector.select();
                if(readChannels == 0) {
                    continue;
                }
                //获取可用的channel
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                //遍历集合
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey selectionKey = iterator.next();

                    //移除set集合当前selectionKey
                    iterator.remove();

                    //如果可读状态
                    if(selectionKey.isReadable()) {
                        readOperator(selector,selectionKey);
                    }
                }
            }
        }catch(Exception e) {

        }
    }
    //处理可读状态操作
    private void readOperator(Selector selector, SelectionKey selectionKey) throws IOException {
        //1 从SelectionKey获取到已经就绪的通道
        SocketChannel socketChannel = (SocketChannel)selectionKey.channel();

        //2 创建buffer
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

        //3 循环读取客户端消息
        int readLength = socketChannel.read(byteBuffer);
        String message = "";
        if(readLength >0) {
            //切换读模式
            byteBuffer.flip();

            //读取内容
            message += Charset.forName("UTF-8").decode(byteBuffer);
        }

        //4 将channel再次注册到选择器上,监听可读状态
        socketChannel.register(selector,SelectionKey.OP_READ);

        //5 把客户端发送消息,广播到其他客户端
        if(message.length()>0) {
            //广播给其他客户端
            System.out.println(message);
        }
    }
}

用于测试的两个客户端:

public class AClient {

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

    public static void main(String[] args) {
        try {
            new ChatClient().startClient("Jack");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

GuochaoHN

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

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

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

打赏作者

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

抵扣说明:

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

余额充值