Java NIO 2.0 之 Java Channels教程

通道是继缓冲区之后的java.nio的第二项主要创新,我们在上一教程中已详细了解到。通道提供与I / O服务的直接连接。通道是一种在字节缓冲区和通道另一端的实体(通常是文件或套接字)之间有效传输数据的介质。通常,通道与操作系统文件描述符具有一对一的关系。通道类提供了维持平台独立性所需的抽象,但仍可以对现代操作系统的本机I / O功能进行建模。通道是网关,通过它可以以最小的开销访问操作系统的本机I / O服务,而缓冲区是通道用来发送和接收数据的内部端点。

NIO频道基础

在层次结构的顶部,有一个Channel接口,如下所示:

package java.nio.channels;
public interface Channel
{
    public boolean isOpen();
    public void close() throws IOException;
}

Channel由于取决于底层平台的各种因素,不同操作系统之间的实现会有很大不同,因此通道API(或接口)仅描述了可以完成的工作。Channel实现通常使用本机代码执行实际工作。通过这种方式,通道接口使您能够以受控且可移植的方式访问低级I / O服务。

从顶级Channel界面可以看到,所有通道只有两个通用的操作:检查通道是否打开(isOpen())和关闭打开的通道(close())。

开通渠道

正如我们已经知道,I / O分为两大类:文件I / O和I / O Stream 。因此,有两种类型的通道也就不足为奇了:文件通道和套接字通道。FileChannelclass和SocketChannelclasses用于处理这两个类别。

FileChannel对象只能通过调用来获得getChannel()方法的开放上RandomAccessFileFileInputStreamFileOutputStream对象。您不能FileChannel直接创建对象。

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

与FileChannels相反,套接字通道具有工厂方法来直接创建新的套接字通道。例如

//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()。如果已经存在,则它们返回与套接字关联的通道。他们从不创造新的渠道。

使用频道

正如我们已经学习到缓冲区的教程一样,该教程介绍了通道与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扩展两个接口),则它是双向的,可以双向传输数据。

如果遍历通道类,您会发现每个文件和套接字通道都实现了这三个接口。就类定义而言,这意味着所有文件和套接字通道对象都是双向的。对于套接字来说,这不是问题,因为它们始终是双向的,但是对于文件来说,这是一个问题。从FileChannel对象的getChannel()方法获得的FileInputStream对象是只读的,但由于FileChannel实现ByteChannel是接口声明的双向。write()在这样的频道上调用将引发未选中状态,NonWritableChannelException因为FileInputStream始终打开具有只读权限的文件。因此,请记住,当通道连接到特定的I / O服务时,通道实例的功能将受到与其连接的服务的特性的限制。连接到只读文件的Channel实例无法写入,即使该Channel实例所属的类可能具有write()方法。程序员要知道如何打开通道,而不要尝试执行底层I / O服务不允许的操作。

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()和写()方法ByteChannelByteBuffer对象作为参数。每个都返回传输的字节数,该字节数可以小于缓冲区中的字节数,甚至为零。缓冲区的位置将增加相同的量。如果执行了部分传输,则可以将缓冲区重新提交给通道以继续传输数据,并从该处停止传输。重复直到缓冲区的hasRemaining()方法返回false。

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

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();
        }
    }
}

通道可以在阻塞或非阻塞模式下运行。非阻塞模式下的通道永远不会使调用线程进入睡眠状态。所请求的操作要么立即完成,要么返回结果表明未完成任何操作。只能将面向 Stream 的通道(例如套接字和管道)置于非阻塞模式。

关闭频道

要关闭频道,请使用其close()方法。与缓冲区不同,通道在关闭后不能重用。开放通道表示与特定I / O服务的特定连接,并封装了该连接的状态。关闭通道后,该连接将丢失,并且该通道不再连接任何东西。

在通道上多次调用close()是无害的。随后在关闭通道上对close()的调用无济于事,并立即返回。

套接字通道可能会花费大量时间关闭,具体取决于系统的网络实现。某些网络协议堆栈可能会在输出耗尽时阻止关闭。

可以使用该isOpen()方法测试通道的打开状态。如果返回true,则可以使用该通道。如果为false,则该通道已关闭,无法再使用。尝试读取,写入或执行任何其他需要通道处于打开状态的操作都会导致ClosedChannelException

 

This entry was posted in   Nio.
Bookmark the   permalink.

saigon has written 1440 articles

One thought on “Java NIO 2.0 之 Java Channels教程

Leave a Reply