简介
- 从 Java 1.4 版本开始引入的一个新的 I/O API,可以替代标准的 Java I/O。
- 提供了与标准 I/O 不同的工作方式,核心是 通道(Channel)、缓冲区(Buffer) 和 选择器(Selector)。
- 支持非阻塞 I/O 操作,非常适合处理大量并发连接,是构建高性能网络应用的基础。
- 多路复用的机制,适合构建高性能服务器应用
核心概念
- Channel:类似于流(Stream),但可以双向读写
- Buffer:一个用于存储数据的容器(本质上是一个数组)
- Selector:用于监听多个 Channel 的事件(如连接、读就绪、写就绪)
- 一个线程可以管理多个 Channel,实现单线程处理多路复用,极大地提高了 I/O 效率
代码示例
java
public class NIOClient {
public static void main(String[] args) throws IOException {
// 1. 连接到服务器
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("localhost", 8080));
socketChannel.configureBlocking(false); // 设置为非阻塞(可选,这里为了演示NIO风格)
System.out.println("已连接到服务器 localhost:8080");
System.out.println("请输入消息(输入 'quit' 退出):");
// 用于读取用户输入
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String userInput;
// 用于读取服务器响应
ByteBuffer buffer = ByteBuffer.allocate(1024);
while ((userInput = reader.readLine()) != null) {
if ("quit".equalsIgnoreCase(userInput)) {
break;
}
// 2. 发送消息到服务器
byte[] messageBytes = userInput.getBytes();
ByteBuffer writeBuffer = ByteBuffer.wrap(messageBytes);
socketChannel.write(writeBuffer);
System.out.println("已发送: " + userInput);
// 3. 读取服务器的响应
buffer.clear(); // 清空缓冲区,准备接收数据
int bytesRead = socketChannel.read(buffer);
if (bytesRead > 0) {
buffer.flip(); // 切换为读模式
byte[] responseBytes = new byte[buffer.remaining()];
buffer.get(responseBytes);
String response = new String(responseBytes);
System.out.println("服务器回复: " + response);
}
}
// 4. 关闭连接
socketChannel.close();
System.out.println("客户端已关闭。");
}
}
java
public class NIOServer {
public static void main(String[] args) throws IOException {
// 1. 打开 Selector
Selector selector = Selector.open();
// 2. 打开 ServerSocketChannel
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false); // 设置为非阻塞模式
serverChannel.bind(new InetSocketAddress(8080));
// 3. 将 ServerSocketChannel 注册到 Selector,监听 ACCEPT 事件
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("NIO 服务器启动,监听端口 8080...");
while (true) {
// 4. 阻塞等待,直到有事件发生
int readyChannels = selector.select();
if (readyChannels == 0) continue;
// 5. 获取就绪的 SelectionKey 集合
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
// 6. 处理不同的事件
if (key.isAcceptable()) {
// 接受新的连接
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel clientChannel = server.accept();
clientChannel.configureBlocking(false); // 设置为非阻塞
clientChannel.register(selector, SelectionKey.OP_READ);
System.out.println("新客户端连接: " + clientChannel.getRemoteAddress());
} else if (key.isReadable()) {
// 读取客户端数据
SocketChannel clientChannel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(256);
int bytesRead = clientChannel.read(buffer);
if (bytesRead > 0) {
buffer.flip();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
String message = new String(data);
System.out.println("收到消息: " + message);
// 回复客户端
ByteBuffer responseBuffer = ByteBuffer.wrap(("Echo: " + message).getBytes());
clientChannel.write(responseBuffer);
} else if (bytesRead == -1) {
// 客户端关闭连接
System.out.println("客户端断开: " + clientChannel.getRemoteAddress());
clientChannel.close();
}
}
// 7. 移除已处理的 key
keyIterator.remove();
}
}
}