Java NIO 通道教程

位置:首页>文章>详情   分类: Java教程 > 编程技术   阅读(265)   2023-06-30 14:19:57

通道是继 buffers 之后 java.nio 的第二个主要新增内容,我们在之前的教程中已经详细了解了这一点。通道提供与 I/O 服务的直接连接。

通道是一种在字节缓冲区和通道另一端的实体(通常是文件或套接字)之间高效传输数据的媒介。

通常,通道与操作系统文件描述符具有一对一的关系。 Channel 类提供了保持平台独立性所需的抽象,但仍然模拟现代操作系统的本机 I/O 功能。

通道是网关,通过它可以以最小的开销访问操作系统的本机 I/O 服务,缓冲区是通道用来发送和接收数据的内部端点。

1.Java NIO通道

在层次结构的顶部,有频道 界面如下所示:

package java.nio.channels;

public interface Channel
{
	public boolean isOpen();
	public void close() throws IOException;
}

由于取决于底层平台的各种因素,Channel 实现在操作系统之间存在根本差异,因此通道 API(或接口)简单地描述了可以做什么。

Channel 实现通常使用本机代码来执行实际工作。通过这种方式,通道接口允许您以受控和可移植的方式访问低级 I/O 服务。

从顶层的Channel界面可以看出,所有通道只有两个共同的操作:检查通道是否打开(isOpen() ) 并关闭一个开放的频道 (close())。

1.1.打开频道

正如我们已经知道的,I/O 分为两大类:

  • 文件输入/输出
  • 流输入/输出

因此,有两种类型的通道也就不足为奇了:文件套接字FileChannel类和SocketChannel类就是用来处理这两个类的。

FileChannel对象只能通过在打开的getChannel()上调用RandomAccessFile方法获得,FileInputStreamFileOutputStream 对象。您不能直接创建 FileChannel 对象。

示例 1:如何获取 FileChannel

RandomAccessFile raf = new RandomAccessFile ("somefile", "r");
FileChannel fc = raf.getChannel();

FileChannel相反,套接字通道有工厂方法来直接创建新的套接字通道。

示例 2:如何创建 SocketChannel

//How to open SocketChannel
SocketChannel sc = SocketChannel.open();
sc.connect(new InetSocketAddress("somehost", someport));

//How to open ServerSocketChannel
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.socket().bind (new InetSocketAddress (somelocalport));

//How to open DatagramChannel
DatagramChannel dc = DatagramChannel.open();

以上方法返回一个相应的套接字通道对象。它们不像 RandomAccessFile.getChannel() 那样是新渠道的来源。如果一个套接字已经存在,它们返回与套接字关联的通道;他们从不创建新渠道。

1.2.使用频道

正如我们已经在缓冲区教程中了解到的那样,通道将数据传入和传出 ByteBuffer 对象。大多数读/写操作都是由下面接口实现的方法执行的。

public interface ReadableByteChannel extends Channel
{
        public int read (ByteBuffer dst) throws IOException;
}

public interface WritableByteChannel extends Channel
{
        public int write (ByteBuffer src) throws IOException;
}

public interface ByteChannel extends ReadableByteChannel, WritableByteChannel
{
}

渠道可以是单向或双向

给定的通道类可能会实现 ReadableByteChannel,它定义了 read() 方法。另一个可能会实现 WritableByteChannel 以提供 write()

实现这些接口中的一个或另一个的类是单向:它只能在一个方向上传输数据。如果一个类实现了两个接口(或扩展了两个接口的 ByteChannel),它就是双向 并且可以双向传输数据。

如果您查看 Channel 类,您会发现每个文件和套接字通道都实现了所有这三个接口。就类定义而言,这意味着所有文件和套接字通道对象都是双向的。

这对套接字不是问题,因为它们总是双向的,但对文件来说是个问题。从 FileChannel 对象的 getChannel() 方法获得的 FileInputStream 对象是只读的,但在接口方面是双向的声明,因为 FileChannel 实现了 ByteChannel

在这样的频道上调用 write() 将抛出未检查的 NonWritableChannelException 因为 FileInputStream 总是以只读权限打开文件。所以请记住,当通道连接到特定的 I/O 服务时,通道实例的功能将受到其所连接服务的特性的限制。

连接到只读文件的 Channel 实例无法写入,即使该 Channel 实例所属的类可能具有 write() 方法。程序员需要知道通道是如何打开的,而不是尝试底层 I/O 服务不允许的操作。

示例 3:我们无法使用任何通道写入只读文件

FileInputStream input = new FileInputStream ("readOnlyFile.txt");
FileChannel channel = input.getChannel();

// This will compile but will throw an IOException 
// because the underlying file is read-only
channel.write (buffer);

read()write()ByteChannel 方法将 ByteBuffer 对象作为参数。每个都返回传输的字节数,它可以小于缓冲区中的字节数,甚至为零。缓冲区的位置将提前相同的量。

如果执行了部分传输,则可以将缓冲区重新提交到通道以继续从中断处传输数据。重复直到缓冲区的 hasRemaining() 方法返回 false。

在下面的示例中,我们将数据从一个通道复制到另一个通道(或从一个文件复制到另一个文件)。

示例 4:在 Java 中将数据从一个通道复制到另一个通道

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;

public class ChannelCopyExample
{
	public static void main(String args[]) throws IOException 
	{
		FileInputStream input = new FileInputStream ("testIn.txt");
		ReadableByteChannel source = input.getChannel();
		
		FileOutputStream output = new FileOutputStream ("testOut.txt");
		WritableByteChannel dest = output.getChannel();

		copyData(source, dest);

		source.close();
		dest.close();
	}

	private static void copyData(ReadableByteChannel src, WritableByteChannel dest) throws IOException 
	{
		ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);

		while (src.read(buffer) != -1) 
		{
			// Prepare the buffer to be drained
			buffer.flip();

			// Make sure that the buffer was fully drained
			while (buffer.hasRemaining()) 
			{
				dest.write(buffer);
			}

			// Make the buffer empty, ready for filling
			buffer.clear();
		}
	}
}

通道可以在阻塞或非阻塞模式下运行。非阻塞模式下的通道永远不会让调用线程进入睡眠状态。请求的操作要么立即完成,要么返回一个结果,表明什么都没做。只有面向流的通道,如套接字和管道,才能置于非阻塞模式。

1.3.关闭频道

要关闭频道,请使用它的 close() 方法。与缓冲区不同,通道在关闭后无法重复使用。开放通道表示与特定 I/O 服务的特定连接并封装该连接的状态。当一个通道关闭时,那个连接就丢失了,通道不再连接到任何东西。

在频道上多次调用 close() 是无害的。在关闭的通道上对 close() 的后续调用不执行任何操作并立即返回。

根据系统的网络实现,套接字通道可能需要很长时间才能关闭。某些网络协议栈可能会在输出耗尽时阻止关闭。

可以使用 isOpen() 方法测试通道的打开状态。如果它返回 true,则可以使用该通道。如果为 false,则通道已关闭,无法再使用。

尝试读取、写入或执行任何其他需要通道处于打开状态的操作将导致 ClosedChannelException

快乐学习!!

地址:https://www.cundage.com/article/java-nio-2-0-channels.html

相关阅读

在普通的 Java 应用程序中,IO 主要发生在输入源和输出目标之间,在 NIO 中,我们也可能需要从一个 channel 非常频繁地转到另一个频道。 将文件数据从一个地方批量传输到另一个地方非...
通道是继 buffers 之后 java.nio 的第二个主要新增内容,我们在之前的教程中已经详细了解了这一点。通道提供与 I/O 服务的直接连接。 通道是一种在字节缓冲区和通道另一端的实体(通...
Java NIO Channels 提供了一个重要的新功能,称为 scatter/gather(在某些圈子中称为 vectored I/O)。分散/聚集是一个简单而强大的概念。 分散/聚集是一种...
了解 Java 内存映射文件,并学习在 RandomAccessFile 和 MemoryMapppedBuffer 的帮助下从内存映射文件读取和写入内容。 1.Java内存映射IO 如果您了解...
Path 类在 Java SE 7 版本中引入,是 java.nio.file 包的主要入口点之一。如果我们的应用程序使用 Java New IO,我们应该更多地了解此类中可用的强大功能。 在本...
我们都知道如何创建任何类的对象。在 Java 中创建对象的最简单方法是使用 new 关键字。让我们探索在 Java 中不使用 new 关键字创建对象的其他方法。 Table of content...
根据应用程序监控公司 New Relic 的一份报告,尽管 Oracle Java 仍然是业界领先的 Java 发行版,但它的受欢迎程度只有两年前的一半。 这一发现包含在该公司 4 月 26 日...
Learn to 将文本文件读入字符串 in Java. Following examples use 文件.readAllBytes(), 文件.行() (to 逐行阅读) and 档案阅读器...
在这个 java 正则表达式 教程中,我们将学习测试是否 输入文本中的单词数介于某个最小和最大限制之间。 以下正则表达式与 限制非空白字符的数量,除了每次重复匹配整个单词而不是单个非空白字符。它...
学习使用不同的解决方案将给定的 byte[] 写入文件。我们将使用为该用例提供简单 API 的 Java NIO、Commons IO 和 Guava API。 1. Java NIO 的 Fi...