NIO(New Input/Output),也称为Java非阻塞IO,是从Java 1.4版本开始引入的一个新的IO API,旨在提供一种比传统的阻塞IO更高效、更灵活的IO操作方式。
NIO支持面向缓冲区的、基于通道的IO操作,其核心组件包括缓冲区(Buffer)、通道(Channel)和选择器(Selector)。
缓冲区是NIO中用于存储数据的对象,它是一个固定大小的内存区域,可以用来读取数据和写入数据。NIO提供了多种类型的缓冲区,如ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer和DoubleBuffer等,用于存储不同类型的数据。
缓冲区的主要属性和方法包括:
缓冲区的主要方法有:
通道是NIO中用于数据读写的通道,它可以与文件、网络套接字等进行交互。通道是双向的,既可以用于读操作也可以用于写操作,并且支持非阻塞模式。与传统的IO流不同,通道与缓冲区配合使用,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。
NIO中主要的通道类型有:
选择器是NIO的一个核心组件,它允许单个线程同时处理多个通道(Channel)的IO事件。通过选择器,可以监听多个通道的状态变化(如连接打开、数据到达等),并在这些事件发生时进行相应的处理。这样,一个线程就可以管理多个网络连接,提高了系统的并发性能。
使用选择器的一般步骤包括:
NIO通过缓冲区、通道和选择器提供了一种高效、灵活的IO操作方式。它适用于需要处理大量并发连接的网络编程和高性能服务器开发等场景。通过合理地使用缓冲区、通道和选择器,可以显著提高系统的并发性能和吞吐量。
NIO(New Input/Output)作为Java中一种新的IO处理方式,相较于传统的BIO(Blocking Input/Output)具有一系列的优点,但同时也存在一些潜在的缺点。
总的来说,NIO作为Java中一种新的IO处理方式,具有显著的性能优势和灵活性。然而,它也带来了一定的学习曲线和编程复杂度。因此,在选择使用NIO时,需要根据实际应用场景和需求进行权衡和考虑。
NIO(New Input/Output)网络编程通常涉及到非阻塞的IO操作,这使得单个线程可以管理多个网络连接。以下是一个更完整的NIO网络编程示例,包括一个简单的服务器和客户端。这个示例将展示如何使用Selector来同时处理多个客户端的连接和数据读取。
服务器端
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; import java.util.Set; public class NIOServer { public static void main(String[] args) throws IOException { Selector selector = Selector.open(); // 打开ServerSocketChannel ServerSocketChannel serverChannel = ServerSocketChannel.open(); serverChannel.configureBlocking(false); // 绑定端口并监听 serverChannel.socket().bind(new InetSocketAddress(8000)); // 注册到selector,监听ACCEPT事件 serverChannel.register(selector, SelectionKey.OP_ACCEPT); // 缓冲区 ByteBuffer buffer = ByteBuffer.allocate(1024); while (true) { // 等待事件发生 int readyChannels = selector.select(); if (readyChannels == 0) continue; // 获取所有已就绪的通道 Set selectedKeys = selector.selectedKeys(); Iterator keyIterator = selectedKeys.iterator(); while (keyIterator.hasNext()) { SelectionKey key = keyIterator.next(); if (key.isAcceptable()) { // 接受新的连接 ServerSocketChannel server = (ServerSocketChannel) key.channel(); SocketChannel client = server.accept(); client.configureBlocking(false); client.register(selector, SelectionKey.OP_READ); System.out.println("Accepted new connection from " + client); } else if (key.isReadable()) { // 读取数据 SocketChannel client = (SocketChannel) key.channel(); buffer.clear(); int bytesRead = client.read(buffer); if (bytesRead > 0) { buffer.flip(); byte[] data = new byte[buffer.remaining()]; buffer.get(data); String received = new String(data, "UTF-8"); System.out.println("Received: " + received); // 这里可以添加将数据写回客户端的逻辑 } } keyIterator.remove(); } } } } 客户端
import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; public class NIOClient { public static void main(String[] args) throws IOException { // 打开SocketChannel SocketChannel client = SocketChannel.open(); client.configureBlocking(false); // 连接到服务器 client.connect(new InetSocketAddress("localhost", 8000)); // 等待连接完成 while (!client.finishConnect()) { // 这里可以做一些其他事情,比如处理其他IO操作 } // 发送数据到服务器 String newData = "Hello from Client"; ByteBuffer buffer = ByteBuffer.wrap(newData.getBytes("UTF-8")); while (buffer.hasRemaining()) { client.write(buffer); } // 关闭SocketChannel client.close(); } } 注意:
- 这个服务器示例在接收到数据后并没有将数据写回客户端。在实际应用中,你可能需要添加这样的逻辑来响应客户端的请求。
- 客户端在发送完数据后立即关闭了SocketChannel。在实际应用中,你可能希望保持连接以接收服务器的响应或进行进一步的通信。
- 服务器端在接收到数据后,会将其转换为字符串并打印出来。在生产环境中,你可能需要处理多种类型的数据和更复杂的协议。
- 这个示例没有处理异常和关闭资源(如Selector和ServerSocketChannel)的逻辑。在实际应用中,你应该在适当的时机关闭这些资源,并处理可能出现的异常。