Java NIO 中的 Path 、Files 和 AsychronousFileChannel (附多人聊天室内代码)(下)

简介: Java NIO 中的 Path 、Files 和 AsychronousFileChannel (附多人聊天室内代码)

Java NIO 综合案例


通过 Java NIO 完成一个多人聊天室的案例:


服务端代码:


// 服务端
public class ChatServer {
    // 服务启动
    public void startServer() throws IOException, InterruptedException {
        // 1、创建 Selector 选择器
        Selector selector = Selector.open();
        // 2、创建 ServerSocketChannel 通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        // 3、为 channel 通道绑定监听端口
        serverSocketChannel.bind(new InetSocketAddress(25000));
        // 设置非阻塞模式
        serverSocketChannel.configureBlocking(false);
        // 4、 把 channel 注册到到 selector 选择器上
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("服务器已经启动成功了");
        // 5、循环,等待新的连接介入
        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();
                // 6、根据就绪状态,调用对应的方法实现具体的操作
                // 6.1 如果 accept 状态
                if (selectionKey.isAcceptable()) {
                    acceptOperator(serverSocketChannel, selector);
                }
                // 6.2 如果可读状态
                else if (selectionKey.isReadable()) {
                    readOperator(selector, selectionKey);
                }
            }
            TimeUnit.SECONDS.sleep(1);
        }
    }
    // 处理可读状态操作
    private void readOperator(Selector selector, SelectionKey selectionKey) throws IOException {
        //1 从 selectionKey 获取已经就绪的通道
        SocketChannel channel = (SocketChannel) selectionKey.channel();
        //2 创建 buffer
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        //3 循环读取客户端发送过来的信息
        int readLen = channel.read(buffer);
        String message = "";
        if (readLen > 0) {
            buffer.flip();
            // 读取内容
            message += Charset.forName("UTF-8").decode(buffer);
        }
        //4 将 channel 再次注册到选择器上,监听可读状态。
        channel.register(selector, SelectionKey.OP_READ);
        //5 把客户端发送的消息,广播到其他的客户端上
        if (message != null && message.length() > 0) {
            // 广播到其他客户端
            System.out.println("message: " + message);
            castOtherClient(message, selector, channel);
        }
    }
    // 广播到其他的客户端
    private void castOtherClient(String message, Selector selector, SocketChannel channel) throws IOException {
        // 1 获取所有已经接入的客户端
        Set<SelectionKey> keys = selector.keys();
        // 2 循环向所有的 channel 广播消息
        for (SelectionKey selectionKey : keys) {
            // 获取里面的每个通道
            SelectableChannel otherChannel = selectionKey.channel();
            // 不需要给自己发送
            if (otherChannel instanceof SocketChannel &&
                channel != otherChannel) {
                ((SocketChannel) otherChannel).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) throws IOException, InterruptedException {
        ChatServer chatServer = new ChatServer();
        chatServer.startServer();
    }
}


客户端代码


// 客户端
// 客户端
public class ChatClient {
    // 启动客户端
    public void startClient(String name) throws IOException {
        // 连接服务器
        SocketChannel socketChannel = SocketChannel.open(
                new InetSocketAddress("127.0.0.1", 25000));
        //接收服务端响应数据
        Selector selector = Selector.open();
        socketChannel.configureBlocking(false);
        socketChannel.register(selector, SelectionKey.OP_READ);
        // 创建线线程
        new Thread(new ClientThread(selector)).start();
        // 向服务器发送消息
        System.out.println("聊天室客户端启动成功!!");
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            String msg = scanner.nextLine();
            if (msg != null && 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 {
            // 循环,等待新的连接介入
            for (; ; ) {
                // 获取 channel 数量
                int readChannels = 0;
                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);
                    }
                }
                // TimeUnit.SECONDS.sleep(1);
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }
    // 处理可读状态操作
    private void readOperator(Selector selector, SelectionKey selectionKey) throws IOException {
        //1 从 selectionKey 获取已经就绪的通道
        SocketChannel channel = (SocketChannel) selectionKey.channel();
        //2 创建 buffer
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        //3 循环读取客户端发送过来的信息
        int readLen = channel.read(buffer);
        String message = "";
        if (readLen > 0) {
            buffer.flip();
            // 读取内容
            message += Charset.forName("UTF-8").decode(buffer);
        }
        //4 将 channel 再次注册到选择器上,监听可读状态。
        channel.register(selector, SelectionKey.OP_READ);
        if (message.length() > 0) {
            // 输出
            System.out.println("收到 message: " + message);
        }
    }
}


相关文章
|
6天前
|
Java
在 Java 中捕获和处理自定义异常的代码示例
本文提供了一个 Java 代码示例,展示了如何捕获和处理自定义异常。通过创建自定义异常类并使用 try-catch 语句,可以更灵活地处理程序中的错误情况。
|
28天前
|
存储 安全 Java
Java Map新玩法:探索HashMap和TreeMap的高级特性,让你的代码更强大!
【10月更文挑战第17天】Java Map新玩法:探索HashMap和TreeMap的高级特性,让你的代码更强大!
57 2
|
28天前
|
存储 Java API
键值对魔法:如何优雅地使用Java Map,让代码更简洁?
键值对魔法:如何优雅地使用Java Map,让代码更简洁?
111 2
|
1月前
|
安全 Java API
Java 17新特性让你的代码起飞!
【10月更文挑战第4天】自Java 8发布以来,Java语言经历了多次重大更新,每一次都引入了令人兴奋的新特性,极大地提升了开发效率和代码质量。本文将带你从Java 8一路走到Java 17,探索那些能让你的代码起飞的关键特性。
76 1
|
21天前
|
XML 安全 Java
Java反射机制:解锁代码的无限可能
Java 反射(Reflection)是Java 的特征之一,它允许程序在运行时动态地访问和操作类的信息,包括类的属性、方法和构造函数。 反射机制能够使程序具备更大的灵活性和扩展性
33 5
Java反射机制:解锁代码的无限可能
|
13天前
|
消息中间件 缓存 Java
java nio,netty,kafka 中经常提到“零拷贝”到底是什么?
零拷贝技术 Zero-Copy 是指计算机执行操作时,可以直接从源(如文件或网络套接字)将数据传输到目标缓冲区, 而不需要 CPU 先将数据从某处内存复制到另一个特定区域,从而减少上下文切换以及 CPU 的拷贝时间。
java nio,netty,kafka 中经常提到“零拷贝”到底是什么?
|
17天前
|
jenkins Java 测试技术
如何使用 Jenkins 自动发布 Java 代码,通过一个电商公司后端服务的实际案例详细说明
本文介绍了如何使用 Jenkins 自动发布 Java 代码,通过一个电商公司后端服务的实际案例,详细说明了从 Jenkins 安装配置到自动构建、测试和部署的全流程。文中还提供了一个 Jenkinsfile 示例,并分享了实践经验,强调了版本控制、自动化测试等关键点的重要性。
48 3
|
22天前
|
存储 安全 Java
系统安全架构的深度解析与实践:Java代码实现
【11月更文挑战第1天】系统安全架构是保护信息系统免受各种威胁和攻击的关键。作为系统架构师,设计一套完善的系统安全架构不仅需要对各种安全威胁有深入理解,还需要熟练掌握各种安全技术和工具。
61 10
|
18天前
|
分布式计算 Java MaxCompute
ODPS MR节点跑graph连通分量计算代码报错java heap space如何解决
任务启动命令:jar -resources odps-graph-connect-family-2.0-SNAPSHOT.jar -classpath ./odps-graph-connect-family-2.0-SNAPSHOT.jar ConnectFamily 若是设置参数该如何设置
|
16天前
|
Java
Java代码解释++i和i++的五个主要区别
本文介绍了前缀递增(++i)和后缀递增(i++)的区别。两者在独立语句中无差异,但在赋值表达式中,i++ 返回原值,++i 返回新值;在复杂表达式中计算顺序不同;在循环中虽结果相同但使用方式有别。最后通过 `Counter` 类模拟了两者的内部实现原理。
Java代码解释++i和i++的五个主要区别