📅  最后修改于: 2023-12-03 14:42:15.467000             🧑  作者: Mango
Java NIO (New IO) 是 Java 1.4 引入的一组 API,用于提供与传统 IO 区别很大的新 IO 编程体验,其主要目标是提高速度和可扩展性,可以作为 Java Socket 工具包的替代选择。在 Java NIO 中,所有交互都是非阻塞的,允许您在一个线程中处理多个操作,从而提高了处理 IO 操作的效率。
这个收集包含有关 Java NIO 中以下几个概念的讨论:
在 Java NIO 中,所有的 IO 操作都需要使用缓冲区。因此学习缓冲区的使用是 Java NIO 编程的基础。缓冲区是一个对象,它包含一些要写入或从其中读取的数据。在 NIO 库中定义了以下缓冲区类:
除了这些基本类型的缓冲区外,Java NIO 还有一些特殊类型的缓冲区,如 MappedByteBuffer 和 DirectByteBuffer。使用一种特殊类型的缓冲区取决于您的具体需求。
在 Java NIO 中,缓冲区具有四个核心属性:
以下是一个简单的例子,说明了如何创建一个 ByteBuffer 并访问其核心属性:
// Create a ByteBuffer with a capacity of 10 bytes
ByteBuffer buffer = ByteBuffer.allocate(10);
// Access the buffer's core attributes
System.out.println("Capacity: " + buffer.capacity()); // Prints 'Capacity: 10'
System.out.println("Limit: " + buffer.limit()); // Prints 'Limit: 10'
System.out.println("Position: " + buffer.position()); // Prints 'Position: 0'
System.out.println("Mark: " + buffer.mark()); // Prints 'Mark: not set'
在 Java NIO 中,通道与流 (Stream) 的概念类似,但也有很大的区别。与流不同,通道是双向的,可在同一个通道上进行读取和写入操作。
Java NIO 库中定义了以下通道类:
以下是一个简单的例子,说明了如何使用 FileChannel 读取文件中的数据:
RandomAccessFile file = new RandomAccessFile("test.txt", "rw");
FileChannel fileChannel = file.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (fileChannel.read(buffer) > 0) {
buffer.flip();
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get());
}
buffer.clear();
}
file.close();
Java NIO 库中还包含一个非常重要的组件:选择器。选择器允许您同时监视多个通道的 IO 事件,从而使单个线程能够管理多个通道。选择器与通道之间的关系可以看作一个三角形:一个选择器与多个通道相关联,每个通道又与一个缓冲区相关联。
要使用选择器,在 Java NIO 中,需要经过以下三个基本步骤:
以下是一个简单的例子,说明了如何使用选择器注册 SocketChannel,并在 Socket 服务端和客户端之间进行通信:
// Create a new Selector
Selector selector = Selector.open();
// Create a new ServerSocketChannel and register it with the Selector
ServerSocketChannel serverSocket = ServerSocketChannel.open();
serverSocket.bind(new InetSocketAddress("localhost", 8080));
serverSocket.configureBlocking(false);
serverSocket.register(selector, SelectionKey.OP_ACCEPT);
// Wait for connections
while (true) {
// Wait for events
selector.select();
Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
// Process each event
while (keys.hasNext()) {
SelectionKey key = keys.next();
if (key.isAcceptable()) {
// Accept the connection
SocketChannel socket = serverSocket.accept();
socket.configureBlocking(false);
socket.register(selector, SelectionKey.OP_READ);
System.out.println("Accepted connection from " + socket);
} else if (key.isReadable()) {
// Read data from the connection
SocketChannel socket = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
socket.read(buffer);
buffer.flip();
System.out.println("Received message '" + new String(buffer.array()) + "' from " + socket);
// Write data back to the client
socket.write(buffer);
buffer.clear();
}
keys.remove();
}
}
Java NIO 对文件的操作也非常方便。以下是一个用于将文件内容写入其他文件的例子:
RandomAccessFile fromFile = new RandomAccessFile("fromFile.txt", "rw");
FileChannel fromChannel = fromFile.getChannel();
RandomAccessFile toFile = new RandomAccessFile("toFile.txt", "rw");
FileChannel toChannel = toFile.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (fromChannel.read(buffer) != -1) {
buffer.flip();
toChannel.write(buffer);
buffer.clear();
}
fromChannel.close();
toChannel.close();
Java NIO 还使您能够在一个线程中同时为多个客户端提供服务。以下是一个简单的例子:
// Create a new ServerSocketChannel and set it to non-blocking mode
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open().bind(new InetSocketAddress(9090));
serverSocketChannel.configureBlocking(false);
// Create a Selector and register the ServerSocketChannel with it
Selector selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
// Wait for connections and process them
while (true) {
if (selector.select() <= 0) {
continue;
}
// Get the keys for the selected channels
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iter = selectedKeys.iterator();
// Process each key
while (iter.hasNext()) {
SelectionKey key = iter.next();
if (key.isAcceptable()) {
// Accept the new connection
ServerSocketChannel serverSocket = (ServerSocketChannel) key.channel();
SocketChannel socket = serverSocket.accept();
socket.configureBlocking(false);
socket.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
// Read data from the connection and write back the response
SocketChannel socket = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
socket.read(buffer);
buffer.flip();
socket.write(buffer);
buffer.clear();
}
iter.remove();
}
}
以下是一个简单的例子,说明了如何使用 Java NIO 复制一个大文件:
FileInputStream inputStream = new FileInputStream("input.txt");
FileOutputStream outputStream = new FileOutputStream("output.txt");
FileChannel inChannel = inputStream.getChannel();
FileChannel outChannel = outputStream.getChannel();
outChannel.transferFrom(inChannel, 0, inChannel.size());
inputStream.close();
outputStream.close();
inChannel.close();
outChannel.close();
Java NIO 还提供了用于序列化和反序列化对象的工具类:
public static void serialize(Object obj, String filename) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filename));
oos.writeObject(obj);
oos.close();
}
public static Object deserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
Object obj = ois.readObject();
ois.close();
return obj;
}
Java NIO 可以非常好地用于编写游戏服务器。以下是一个简单的例子,说明了如何使用 Java NIO 实现一个简单的文字游戏服务器:
// Create a new ServerSocketChannel and set it to non-blocking mode
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open().bind(new InetSocketAddress(9090));
serverSocketChannel.configureBlocking(false);
// Create a Selector and register the ServerSocketChannel with it
Selector selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
// Create a list to store the connected clients
List<Client> clients = new ArrayList<>();
// Wait for connections and process them
while (true) {
if (selector.select() <= 0) {
continue;
}
// Get the keys for the selected channels
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iter = selectedKeys.iterator();
// Process each key
while (iter.hasNext()) {
SelectionKey key = iter.next();
if (key.isAcceptable()) {
// Accept the new client
ServerSocketChannel serverSocket = (ServerSocketChannel) key.channel();
SocketChannel socket = serverSocket.accept();
socket.configureBlocking(false);
socket.register(selector, SelectionKey.OP_READ);
clients.add(new Client(socket));
} else if (key.isReadable()) {
// Read data from the client and broadcast it to all other clients
SocketChannel socket = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
socket.read(buffer);
buffer.flip();
String message = new String(buffer.array()).trim();
for (Client client : clients) {
if (client.socket != socket) {
client.socket.write(ByteBuffer.wrap((message + "\n").getBytes()));
}
}
buffer.clear();
}
iter.remove();
}
}
// The Client class
class Client {
public SocketChannel socket;
public Client(SocketChannel socket) {
this.socket = socket;
}
}
Netty 是一个用于创建高性能、可扩展的网络应用程序的框架。Netty 基于 Java NIO 构建,提供了异步、事件驱动和可扩展的网络应用程序开发 API,使您可以轻松地创建高性能和可伸缩的网络应用程序。以下是一个简单的例子,说明了如何使用 Netty 实现一个简单的 Echo 服务器:
// Create a new ServerBootstrap
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
// Configure the ServerBootstrap
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new EchoServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
// Start the server
ChannelFuture channelFuture = serverBootstrap.bind(8080).sync();
System.out.println("Server started");
// Wait for the server to close
channelFuture.channel().closeFuture().sync();
System.out.println("Server closed");
// The EchoServerHandler class
class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buffer = (ByteBuf) msg;
byte[] bytes = new byte[buffer.readableBytes()];
buffer.getBytes(0, bytes);
System.out.println("Received message '" + new String(bytes) + "' from " + ctx.channel().remoteAddress());
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
以上就是 Java NIO 的一些概念和使用示例。希望本文能够对您有所帮助。