NIO摘录
NIO,一种基于通道和缓冲区的I/O方式,可以使用native函数库直接分配堆外内存,然后通过一个存储在java
堆的DirectBteBuffer对象作为这块内存的引用进行操作,避免了再java堆和native堆中来回复制数据。
NIO是一种同步非阻塞的IO模型。同步是指线程不断轮询IO事件是否就绪,非阻塞指线程在等待IO的时候,可以
同时做其他任务。同步的核心是Selector,Selector代替了线程本身轮询IO事件,避免了阻塞同时减少了不必要的
线程消耗;非阻塞的核心就是通道和缓冲区,当IO事件就绪时,可以通过写道缓冲区,保证IO的成功,而无需线程
阻塞式地等待。
Buffer
为什么NIO是基于缓冲区的IO方式,因为当一个连接建立完成后,IO数据未必会马上到达,为了当数据达到时能够正确
完成IO操作,在BIO(阻塞IO)中,等待IO的线程必须被阻塞,以全天候地进行IO操作。为了解决这种IO方式的低效问题,
引入了缓冲区的概念,当数据达到时,可以预先被写入缓冲区,再由缓冲区交给线程,因此线程无需阻塞等待IO
通道
当执行:SocketChannel.write(Buffer),将一个buffer写到一个通道中。通道来说相对比较抽象。通道是I/O传输发生时通过的入口
而缓冲区是这些数据传输的来源或目标。对于离开缓冲区的传输,你想传递出去的数据被置于一个缓冲区,被传送到通道。对于
传回缓冲区的传输,一个通道将数据放置于你所提供的缓冲区中。
例如有个服务器通道ServerSocketChannel serverChannel,一个客户端通道SocketChannel clientChannel;服务器缓存区
serverBuffer,客户端缓冲区clientBuffer。当服务器想向客户端发送数据时,需要调用clientChannel.write(serverBuffer)。当
客户端要读时,调用clientChannel.read(clientBuffer);当客户端向服务器发送数据时,需要调用serverChannel.write(clientBuffer)
当服务器要读时,调用serverChannel.read(serverBuffer); 可以理解为在NIO中,如果想将data发送到目标端,则需要将存储该
data的buffer,写入到目标端channel中,然后再从channel中读取数据到目标端的buffer中。
Selector
通道和缓冲区的机制,使得现场无需阻塞等待IO事件的就绪,但总的要有人来监管这些IO事件。 这就由selector来完成。selector允许
单个现场处理多个channel,如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用selector就比较方便
要使用selector,的向selector注册channel,然后调用它的select方法,这个方法会一直阻塞到某个注册的通道有事件就绪,这就是轮询。
一旦这个方法返回,线程就可以处理这些事件。
selector中注册的感兴趣事件有: OP_ACCEPT OP_CONNECT OP_READ OP_WRITE
简单基于NIO方式实现server/client示例

package com.exe.learn.demo.nio; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; public class ServerNio { private Selector selector; public void initServer(int port) throws IOException { ServerSocketChannel serverChannel = ServerSocketChannel.open(); //设置非阻塞模式 serverChannel.configureBlocking(false); serverChannel.socket().bind(new InetSocketAddress(port)); this.selector = Selector.open(); //将selector注册到服务端 serverChannel.register(selector, SelectionKey.OP_ACCEPT); } public void listen() throws IOException { System.out.println("服务端启动》》》》》"); while(true) { selector.select(); Iterator<SelectionKey> ite = this.selector.selectedKeys().iterator(); while(ite.hasNext()) { SelectionKey key = (SelectionKey) ite.next(); ite.remove(); if(key.isAcceptable()) { ServerSocketChannel server = (ServerSocketChannel) key.channel(); SocketChannel socketChannel = server.accept(); socketChannel.configureBlocking(false); socketChannel.write(ByteBuffer.wrap(new String("来自服务端的信息").getBytes())); socketChannel.register(this.selector, SelectionKey.OP_READ); }else if(key.isReadable()){ readFromClient(key); } } } } private void readFromClient(SelectionKey key) throws IOException { SocketChannel server = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(10); server.read(buffer); byte[] data = buffer.array(); String msg = new String(data).trim(); System.out.println("来自客户端的信息" + msg); server.write(ByteBuffer.wrap(new String(msg).getBytes())); } public static void main(String[] args) throws IOException { ServerNio server = new ServerNio(); server.initServer(1024); server.listen(); } }

package com.exe.learn.demo.nio; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.util.Iterator; public class ClientNio { private Selector selector; public void initClient(String ip, int port) throws IOException { SocketChannel socketChannel = SocketChannel.open(); socketChannel.configureBlocking(false); this.selector = Selector.open(); //连接server端 socketChannel.connect(new InetSocketAddress(ip, port)); socketChannel.register(selector, SelectionKey.OP_CONNECT); } public void listen() throws IOException { System.out.println("客户端启动》》》》》》"); while(true) { selector.select(); Iterator<SelectionKey> ite = this.selector.selectedKeys().iterator(); while(ite.hasNext()) { SelectionKey key = ite.next(); ite.remove(); if(key.isConnectable()) { SocketChannel socketChannel = (SocketChannel) key.channel(); if(socketChannel.isConnectionPending()) { socketChannel.finishConnect(); } socketChannel.configureBlocking(false); socketChannel.write(ByteBuffer.wrap(new String("向服务器发送信息了".getBytes(), "UTF-8").getBytes())); socketChannel.register(selector, SelectionKey.OP_READ); }else if(key.isReadable()) { readFromServer(key); } } } } private void readFromServer(SelectionKey key) throws IOException { SocketChannel server = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(10); server.read(buffer); byte[] data = buffer.array(); String msg = new String(data).trim(); System.out.println("来自服务端的信息" + msg); server.write(ByteBuffer.wrap(new String(msg).getBytes())); } public static void main(String[] args) throws IOException { ClientNio client = new ClientNio(); client.initClient("127.0.0.1", 1024); client.listen(); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?