我的生物系学妹也能听懂的Java NIO中Write事件

简介: 我的生物系学妹也能听懂的Java NIO中Write事件

1 NIO Server端

1.1 多路复用开发一般步骤

//打开选择器
Selector selector = Selector.open();
//打开通到
ServerSocketChannel socketChannel = ServerSocketChannel.open();
//配置非阻塞模型
socketChannel.configureBlocking(false);
//绑定端口
socketChannel.bind(new InetSocketAddress(8080));
//注册事件,OP_ACCEPT只适用于ServerSocketChannel 
socketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
    selector.select();
    Set<SelectionKey> selectionKeys = selector.selectedKeys();
    Iterator<SelectionKey> iter = selectionKeys.iterator();
    while(iter.hasNext()) {
        SelectionKey key = iter.next();
        if(key.isAcceptable()) {
            SocketChannel channel = ((ServerSocketChannel)key.channel()).accept();
            channel.configureBlocking(false);
            channel.register(selector,SelectionKey.OP_READ);
        }
        if(key.isWritable()) {
        }
        if(key.isReadable()) {
            SocketChannel channel = (SocketChannel) key.channel();
            ByteBuffer readBuffer = ByteBuffer.allocate(1024);
            channel.read(readBuffer);
            readBuffer.flip();
            // handler Buffer
            // 一般是响应客户端的数据
            // 直接是write写不就完事了嘛,为啥需要write事件?
            // channel.write(...)
        }
        iter.remove();
    }
}

1.2 解惑写事件

对NIO的写操作:

  • 为什么要注册写事件
  • 何时注册写事件
  • 为什么写完之后要取消注册写事件


如果有channel在Selector上注册了SelectionKey.OP_WRITE,在调用selector.select();时,系统会检查内核写缓冲区是否可写:


如果可写,selector.select();立即返回,进入key.isWritable()

何时不可写?比如缓冲区已满,channel调用了shutdownOutPut等


当然除了注册写事件,你也可以在channel直接调用write(…),也可以将数据发出去,但这样不够灵活,而且可能浪费CPU。

比如服务端需要发送一个200M的Buffer,看看是否使用OP_WRITE事件的区别。



不使用事件

程序运行到这会等到200M文件发送完成后才继续往下执行,不符合异步事件模型的思想。若缓冲区一直处不可写状态,则该过程一直在这里死循环,浪费CPU。

// 200M的Buffer
ByteBuffer buffer = .... 
while(buffer.hasRemaining()) {
    // 该方法只会写入小于socket's output buffer空闲区域的任何字节数
    // 并返回写入的字节数,可能是0字节
    channel.write(buffer);
}


使用事件

if(key.isReadable()) {
  // 200M Buffer
    ByteBuffer buffer = .... 
    // 注册写事件
    key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
    // 绑定Buffer
    key.attach(buffer);
}
// 可写分支
if(key.isWritable()) {
    ByteBuffer buffer = (ByteBuffer) key.attachment();
    SocketChannel channel = (SocketChannel) key.channel();
    if (buffer.hasRemaining()) {
        channel.write(buffer)
    } else {
        // 发送完了就取消写事件,否则下次还会进入写事件分支(因为只要还可写,就会进入)
        key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
    }
}

使用事件


要触发写事件,需要先向 selector 注册该通道的写事件,跟注册读事件一样,当底层写缓冲区有空闲就会触发写事件了,而一般来说底层的写缓冲区大部分都是空闲的。所以一般只要注册了写事件,就会立马触发了,为了避免 cpu 空转,在写操作完成后需要把写事件取消掉,然后下次再有写操作时重新注册写事件。


2 NIO Client端

开发的一般步骤

// 打开选择器
Selector selector = Selector.open();
// 打开通道
SocketChannel socketChannel = SocketChannel.open();
// 配置非阻塞模型
socketChannel.configureBlocking(false);
// 连接Server
socketChannel.connect(new InetSocketAddress("127.0.0.1",8080));
// 注册事件
socketChannel.register(selector, SelectionKey.OP_CONNECT | SelectionKey.OP_READ);
// 循环处理
while (true) {
    selector.select();
    Set<SelectionKey> keys = selector.selectedKeys();
    Iterator<SelectionKey> iter = keys.iterator();
    while(iter.hasNext()) {
        SelectionKey key = iter.next();
        if(key.isConnectable()) {
            // 连接建立或者连接建立不成功
            SocketChannel channel = (SocketChannel) key.channel();
            // 完成连接建立
            if(channel.finishConnect()) {
            }
        }
        if(key.isReadable()) {
            SocketChannel channel = (SocketChannel) key.channel();
            ByteBuffer buffer = ByteBuffer.allocate(500 * 1024 * 1024);
            buffer.clear();
            channel.read(buffer);
            // buffer Handler
        }
        iter.remove();
    }
}

起初对OP_CONNECT事件还有finishConnect不理解,OP_CONNECT事件何时触发,特别是为什么要在key.isConnectable()分支里调用finishConnect方法后才能进行读写操作。


首先,在non-blocking模式下调用socketChannel.connect(new InetSocketAddress(“127.0.0.1”,8080));连接远程主机,如果连接能立即建立就像本地连接一样,该方法会立即返回true,否则该方法会立即返回false,然后系统底层进行三次握手建立连接。连接有两种结果,一种是成功连接,第二种是异常,但是connect方法已经返回,无法通过该方法的返回值或者是异常来通知用户程序建立连接的情况,所以由OP_CONNECT事件和finishConnect方法来通知用户程序。不管系统底层三次连接是否成功,selector都会被唤醒继而触发OP_CONNECT事件,如果握手成功,并且该连接未被其他线程关闭,finishConnect会返回true,然后就可以顺利的进行channle读写。如果网络故障,或者远程主机故障,握手不成功,用户程序可以通过finishConnect方法获得底层的异常通知,进而处理异常。


目录
相关文章
|
2月前
|
Java 开发者 Spring
java springboot监听事件和处理事件
通过上述步骤,开发者可以在Spring Boot项目中轻松实现事件的发布和监听。事件机制不仅解耦了业务逻辑,还提高了系统的可维护性和扩展性。掌握这一技术,可以显著提升开发效率和代码质量。
100 33
|
22天前
|
缓存 网络协议 Java
JAVA网络IO之NIO/BIO
本文介绍了Java网络编程的基础与历史演进,重点阐述了IO和Socket的概念。Java的IO分为设备和接口两部分,通过流、字节、字符等方式实现与外部的交互。
|
2月前
|
Java 开发者 Spring
java springboot监听事件和处理事件
通过上述步骤,开发者可以在Spring Boot项目中轻松实现事件的发布和监听。事件机制不仅解耦了业务逻辑,还提高了系统的可维护性和扩展性。掌握这一技术,可以显著提升开发效率和代码质量。
91 13
|
2月前
|
Java Spring
Java Spring Boot监听事件和处理事件
通过上述步骤,我们可以在Java Spring Boot应用中实现事件的发布和监听。事件驱动模型可以帮助我们实现组件间的松耦合,提升系统的可维护性和可扩展性。无论是处理业务逻辑还是系统事件,Spring Boot的事件机制都提供了强大的支持和灵活性。希望本文能为您的开发工作提供实用的指导和帮助。
119 15
|
2月前
|
Java 开发者 Spring
Java Springboot监听事件和处理事件
通过这些内容的详细介绍和实例解析,希望能帮助您深入理解Spring Boot中的事件机制,并在实际开发中灵活应用,提高系统的可维护性和扩展性。
69 7
|
3月前
|
监控 Java API
探索Java NIO:究竟在哪些领域能大显身手?揭秘原理、应用场景与官方示例代码
Java NIO(New IO)自Java SE 1.4引入,提供比传统IO更高效、灵活的操作,支持非阻塞IO和选择器特性,适用于高并发、高吞吐量场景。NIO的核心概念包括通道(Channel)、缓冲区(Buffer)和选择器(Selector),能实现多路复用和异步操作。其应用场景涵盖网络通信、文件操作、进程间通信及数据库操作等。NIO的优势在于提高并发性和性能,简化编程;但学习成本较高,且与传统IO存在不兼容性。尽管如此,NIO在构建高性能框架如Netty、Mina和Jetty中仍广泛应用。
70 3
|
3月前
|
存储 监控 Java
Java的NIO体系
通过本文的介绍,希望您能够深入理解Java NIO体系的核心组件、工作原理及其在高性能应用中的实际应用,并能够在实际开发中灵活运用这些知识,构建高效的Java应用程序。
77 5
|
4月前
|
设计模式 消息中间件 搜索推荐
Java 设计模式——观察者模式:从优衣库不使用新疆棉事件看系统的动态响应
【11月更文挑战第17天】观察者模式是一种行为设计模式,定义了一对多的依赖关系,使多个观察者对象能直接监听并响应某一主题对象的状态变化。本文介绍了观察者模式的基本概念、商业系统中的应用实例,如优衣库事件中各相关方的动态响应,以及模式的优势和实际系统设计中的应用建议,包括事件驱动架构和消息队列的使用。
|
4月前
|
消息中间件 缓存 Java
java nio,netty,kafka 中经常提到“零拷贝”到底是什么?
零拷贝技术 Zero-Copy 是指计算机执行操作时,可以直接从源(如文件或网络套接字)将数据传输到目标缓冲区, 而不需要 CPU 先将数据从某处内存复制到另一个特定区域,从而减少上下文切换以及 CPU 的拷贝时间。
java nio,netty,kafka 中经常提到“零拷贝”到底是什么?
|
6月前
|
存储 网络协议 Java
Java NIO 开发
本文介绍了Java NIO(New IO)及其主要组件,包括Channel、Buffer和Selector,并对比了NIO与传统IO的优势。文章详细讲解了FileChannel、SocketChannel、ServerSocketChannel、DatagramChannel及Pipe.SinkChannel和Pipe.SourceChannel等Channel实现类,并提供了示例代码。通过这些示例,读者可以了解如何使用不同类型的通道进行数据读写操作。
101 0
Java NIO 开发