Java NIO - 基础详解¶
新的输入/输出 (NIO) 库是在 JDK 1.4 中引入的,弥补了原来的 I/O 的不足,提供了高速的、面向块的 I/O。
而NIO把IO抽象成块每次IO操作的单位都是一个块,块被读入内存之后就是一个byte[],NIO一次可以读或写多个字节。
通道与缓冲区¶
通道(Channel)¶
用于源节点与目标节点的连接。在 Java NIO 中,通道用于传输数据。Channel 本身不存储数据,因此需要配合缓冲区进行传输。
缓冲区(Buffer)¶
在 Java NIO 中,负责数据的存取。缓冲区就是数组。用于存储不同数据类型的数据,如:int、char、double、float、long等。缓冲区在创建时,不包含任何数据。需要利用 put() 方法添加数据,使用 get() 方法获取数据。
选择器(Selector)¶
选择器(Selector)是 SelectableChannel 对象的多路复用器。Selector 仅用于处理 SelectableChannel,因此可以理解为是多个通道的选择器。
文件NIO示例¶
import java.io.*;
public class NIOExample {
public static void main(String[] args) throws Exception {
File file = new File("test.txt");
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos);
bos.write("Hello World!".getBytes());
bos.close();
System.out.println("文件已写入");
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
byte[] buffer = new byte[1024];
int length = bis.read(buffer);
while (length != -1) {
System.out.println(new String(buffer, 0, length));
length = bis.read(buffer);
}
bis.close();
file.delete();
System.out.println("文件已删除");
}
}
选择器示例¶
客户端¶
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 NIOExampleClient {
public static void main(String[] args) throws IOException {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
boolean isConnected = socketChannel.connect(new InetSocketAddress("localhost", 8080));
while (!isConnected) {
Selector selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_CONNECT);
selector.select();
Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
while (keys.hasNext()) {
SelectionKey key = keys.next();
keys.remove();
if (key.isConnectable()) {
SocketChannel channel = (SocketChannel) key.channel();
isConnected = channel.finishConnect();
}
}
}
System.out.println("已连接到服务器");
while (true) {
System.out.print("请输入消息:");
String message = new java.util.Scanner(System.in).nextLine();
ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
socketChannel.write(buffer);
System.out.println("已发送消息:" + message);
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
int bytesRead = socketChannel.read(readBuffer);
if (bytesRead > 0) {
readBuffer.flip();
byte[] bytes = new byte[readBuffer.remaining()];
readBuffer.get(bytes);
String receivedMessage = new String(bytes);
System.out.println("收到服务器消息:" + receivedMessage);
}
if (message.equals("exit")) {
break;
}
}
}
}
服务端¶
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 NIOExampleServer {
public static void main(String[] args) throws IOException {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.bind(new InetSocketAddress(8080));
Selector selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("服务器已启动,等待连接...");
while (true) {
selector.select();
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel client = server.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
System.out.println("新客户端连接: " + client);
} else if (key.isReadable()) {
readData(key);
}
}
}
}
private static void readData(SelectionKey key) throws IOException {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int readBytes = client.read(buffer);
if (readBytes > 0) {
buffer.flip();
String data = new String(buffer.array(), 0, readBytes);
System.out.println("接收到消息: " + data);
ByteBuffer outBuffer;
switch (data) {
case "你好":
outBuffer = ByteBuffer.wrap("你好".getBytes());
break;
case "你好吗":
outBuffer = ByteBuffer.wrap("我很好".getBytes());
break;
case "你叫什么名字":
outBuffer = ByteBuffer.wrap("我叫小明".getBytes());
break;
default:
outBuffer = ByteBuffer.wrap("我不明白".getBytes());
}
client.write(outBuffer);
}
}
}