打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Java nio入门教程详解(0015)
userphoto

2016.09.30

关注
3.1.2 使用通道
  我们在第二章的学习中已经知道了,通道将数据传输给ByteBuffer对象或者从ByteBuffer对象获取数据进行传输。将图 3-2 中大部分零乱内容移除可以得到图3-3所示的UML类图。子接口API代码如下:
  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
  {
  }
  
图 3-3 ByteChannel 接口
  通道可以是单向(unidirectional)或者双向的(bidirectional)。一个channel类可能实现定义read()方法的ReadableByteChannel接口,而另一个channel类也许实现WritableByteChannel接口以提供write()方法。实现这两种接口其中之一的类都是单向的,只能在一个方向上传输数据。如果一个类同时实现这两个接口,那么它是双向的,可以双向传输数据。
  图 3-3 显示了一个ByteChannel接口,该接口引申出了ReadableByteChannel和WritableByteChannel两个接口。ByteChannel接口本身并不定义新的API方法,它是一种用来聚集它自己以一个新名称继承的多个接口的便捷接口。根据定义,实现ByteChannel接口的通道会同时实现ReadableByteChannel和WritableByteChannel两个接口,所以此类通道是双向的。这是简化类定义的语法糖(syntactic sugar),它使得用操作器(operator)实例来测试通道对象变得更加简单。
  这是一种好的类设计技巧,如果您在写您自己的Channel实现的话,您可以适当地实现这些接口。不过对于使用java.nio.channels包中标准通道类的程序员来说,这些接口并没有太大的吸引力。假如您快速回顾一下图 3-2 或者向前跳跃到关于file和socket通道的章节,您将发现每一个file或socket通道都实现全部三个接口。从类定义的角度而言,这意味着全部file和socket通道对象都是双向的。这对于sockets不是问题,因为它们一直都是双向的,不过对于files却是个问题了。
  我们知道,一个文件可以在不同的时候以不同的权限打开。从FileInputStream对象的getChannel()方法获取的FileChannel对象是只读的,不过从接口声明的角度来看却是双向的,因为FileChannel实现ByteChannel接口。在这样一个通道上调用write()方法将抛出未经检查的NonWritableChannelException异常,因为FileInputStream对象总是以read-only的权限打开文件。
  通道会连接一个特定I/O服务且通道实例(channel instance)的性能受它所连接的 I/O 服务的特征限制,记住这很重要。一个连接到只读文件的Channel实例不能进行写操作,即使该实例所属的类可能有write()方法。基于此,程序员需要知道通道是如何打开的,避免试图尝试一个底层 I/O服务不允许的操作。
  // A ByteBuffer named buffer contains data to be written
  FileInputStream input = new FileInputStream (fileName);
  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的read()和write()方法使用ByteBuffer对象作为参数。两种方法均返回已传输的字节数,可能比缓冲区的字节数少甚至可能为零。缓冲区的位置也会发生与已传输字节相同数量的前移。如果只进行了部分传输,缓冲区可以被重新提交给通道并从上次中断的地方继续传输。该过程重复进行直到缓冲区的hasRemaining()方法返回false值。例 3-1 表示了如何从一个通道复制数据到另一个通道。
  package com.ronsoft.books.nio.channels;
  import java.nio.ByteBuffer;
  import java.nio.channels.ReadableByteChannel;
  import java.nio.channels.WritableByteChannel;
  import java.nio.channels.Channels;
  import java.io.IOException;
  /**
  * Test copying between channels.
  * @author Ron Hitchens(ron@ronsoft.com)
  */
  public class ChannelCopy{
  /**
  * This code copies data from stdin to stdout. Like the 'cat'
  * command, but without any useful options.
  */
  public static void main (String [] argv) throws IOException{
  ReadableByteChannel source = Channels.newChannel(System.in);
  WritableByteChannel dest = Channels.newChannel(System.out);
  channelCopy1(source, dest);
  // alternatively, call channelCopy2 (source, dest);
  source.close();
  dest.close();
  }
  /**
  * Channel copy method 1. This method copies data from the src
  * channel and writes it to the dest channel until EOF on src.
  * This implementation makes use of compact( ) on the temp buffer
  * to pack down the data if the buffer wasn't fully drained. This
  * may result in data copying, but minimizes system calls. It also
  * requires a cleanup loop to make sure all the data gets sent.
  */
  private static void channelCopy1 (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();
  // Write to the channel; may block
  dest.write(buffer);
  // If partial transfer, shift remainder down
  // If buffer is empty, same as doing clear()
  buffer.compact();
  }
  // EOF will leave buffer in fill state
  buffer.flip();
  // Make sure that the buffer is fully drained
  while (buffer.hasRemaining()) {
  dest.write(buffer);
  }
  }
  /**
  * Channel copy method 2. This method performs the same copy, but
  * assures the temp buffer is empty before reading more data. This
  * never requires data copying but may result in more systems calls.
  * No post-loop cleanup is needed because the buffer will be empty
  * when the loop is exited.
  */
  private static void channelCopy2 (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();
  }
  }
  }
  通道可以以阻塞(blocking)或非阻塞(nonblocking)模式运行。非阻塞模式的通道永远不会让调用的线程休眠。请求的操作要么立即完成,要么返回一个结果表明未进行任何操作。只有面向流的(stream-oriented)的通道,如 sockets 和 pipes 才能使用非阻塞模式。
  从图 3-2 可以看出,socket通道类从SelectableChannel引申而来。从SelectableChannel引申而来的类可以和支持有条件的选择(readiness selectio)的选择器(Selectors)一起使用。将非阻塞I/O 和选择器组合起来可以使您的程序利用多路复用 I/O(multiplexed I/O)。选择和多路复用将在第四章中予以讨论。关于怎样将sockets置于非阻塞模式的细节会在3.5节中涉及。
  Java nio入门教程详解(十六)
  0 0
  我们认为:用户的主要目的,是为了获取有用的信息,而不是来点击广告的。因此本站将竭力做好内容,并将广告和内容进行分离,确保所有广告不会影响到用户的正常阅读体验。用户仅凭个人意愿和兴趣爱好点击广告。
  我们坚信:只有给用户带来价值,用户才会给我们以回报。
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Java NIO中的通道Channel(一)通道基础
JavaNIO中的Channel概述
(转)Java NIO使用及原理分析 (一)
Java NIO与IO的区别和比较
Netty了解与小试
Java NIO:NIO概述
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服