基于NIO实现的尬聊Demo

分类专栏:
NIO相关

文章标签:
Java自学
NIO
原创

尬聊聊天室

代码如下

服务端代码
/**
 * 聊天室服务端
 * @author:PZ
 * @Date: 2020/12/1 16 27
 */

public class ChatRoomServer {

    /**
     * 服务端通道
     */
    private ServerSocketChannel channel;

    /**
     * 多路复用选择器
     */
    private Selector selector;

    public ChatRoomServer(){
        try {
            channel = ServerSocketChannel.open();
            selector = Selector.open();
            //设置端口为7777
            SocketAddress address = new InetSocketAddress(7777);
            channel.socket().bind(address);
            //设置为非阻塞
            channel.configureBlocking(false);
            // 注册通道的可接收事件
            channel.register(selector, SelectionKey.OP_ACCEPT);
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    /**
     * 监听客户端的变化
     *
     * @throws Exception
     */
    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();

                //如果key是可连接事件
                if (key.isAcceptable()){
                    SocketChannel clientChannel = channel.accept();
                    //设置为非阻塞
                    clientChannel.configureBlocking(false);
                    //聊天室客户端注册可读事件
                    clientChannel.register(selector,SelectionKey.OP_READ);
                    System.out.println("欢迎"+clientChannel.socket().getRemoteSocketAddress()+"上线啦");
                    continue;
                }
                //如果key是可读的 执行读取数据方法
                if (key.isReadable()){
                    readData(key);
                }
            }
        }
    }

    /**
     * 监听到读取事件执行方法
     *
     * @param key
     */
    private void readData(SelectionKey key){

        SocketChannel channel = null;

        try {
            channel = (SocketChannel) key.channel();
            //指定大小
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            //num代表写入数组长度
            int num = channel.read(buffer);

            if (num > 0) {
                String msg = new String(buffer.array());
                msg = msg + "from " + channel.socket().getRemoteSocketAddress();
                System.out.println("msg:" + msg);
                //再将数据广播给其他的客户端,不包括发送数据的客户端自身
                sendToOther(msg, channel);
            }
        } catch (Exception e) {
            // 没有获取到通道,即为用户下线了
            System.out.println("用户" + channel.socket().getRemoteSocketAddress() + "退出啦");
            // 取消注册关系
            key.cancel();
            // 关闭通道
            try {
                channel.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }

    /**
     * 将数据广播给其他用户
     *
     * @param msg
     * @param selfChannel
     * @throws Exception
     */
    private void sendToOther(String msg, SocketChannel selfChannel) throws Exception {

        // 找到所有的通道,相当于找到所有在线用户
        Set<SelectionKey> set = selector.keys();

        for (SelectionKey selectionKey : set) {

            Channel channel = selectionKey.channel();

            /**
             * 判断这是一个用户,并且不是发送消息的那个人
             * 代码即为
             * 这是一个SocketChannel,并且不等价于selfChannel
             */
            if (channel instanceof SocketChannel && channel != selfChannel) {
                SocketChannel socketChannel = (SocketChannel) channel;
                // wrap方法,直接将array数据存放到缓冲区
                ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
                socketChannel.write(buffer);
            }
        }
    }

    public static void main(String[] args) throws Exception {
        ChatRoomServer server = new ChatRoomServer();
        server.listenClient();
    }
}


客户端代码
/**
 * @author:PZ
 * @Date: 2020/12/1 16 28
 */

public class ChatRoomClient {

    /**
     * 客户端通道
     */
    private SocketChannel channel;

    /**
     * 多路复用选择器
     */
    private Selector selector;

    public ChatRoomClient(){
        try {
            selector = Selector.open();
            //连接的ip与端口号
            SocketAddress address = new InetSocketAddress("127.0.0.1",7777);
            channel = SocketChannel.open(address);
            //设置为非阻塞
            channel.configureBlocking(false);
            //注册可读事件
            channel.register(selector, SelectionKey.OP_READ);
            System.out.println("欢迎"+channel.getLocalAddress()+"登录");
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    /**
     * 发数据
     * @param msg
     */
    public void sendData(String msg){

        ByteBuffer byteBuffer = ByteBuffer.wrap(msg.getBytes());
        try {
            channel.write(byteBuffer);
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    /**
     * 监听到channel有读事件发生
     */
    public void readData(){

        try {
            //选择器查找是否有事件处理
            int num = selector.select();
            if (num>0){
                //获取要处理事件的集合
                Set<SelectionKey>set = selector.selectedKeys();
                Iterator<SelectionKey>iterator = set.iterator();

                while (iterator.hasNext()){
                    SelectionKey key = iterator.next();
                    //避免重复处理
                    iterator.remove();
                    //如果key是可连接事件
                    if (key.isReadable()){
                        SocketChannel socketChannel = (SocketChannel)key.channel();
                        //指定大小
                        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                        socketChannel.read(byteBuffer);
                        String msg = new String(byteBuffer.array());
                        System.out.println(msg);
                    }
                }
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        final ChatRoomClient client = new ChatRoomClient();
        new Thread(){
            @Override
            public void run(){
                while (true){
                    client.readData();

                    try {
                        sleep(2000);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }
        }.start();

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

执行结果

1)服务端启动监听

服务端启动监听

2)客户端登录

客户端登录

3)服务端接收到客户端信息

服务端接收到客户端信息

4)客户端退出

客户端退出

####以上图片来自我CSDN博客

  • 作者:潘震
  • 评论

    pz
    博主
    尬聊聊天室^_^
    留言