NIO入门系列之第6章:分散和聚集

第6章 分散和聚集

6.1  概述

分散/聚集 I/O 是使用多个而不是单个缓冲区来保存数据的读写方法。

一个分散的读取就像一个常规通道读取,只不过它是将数据读到一个缓冲区数组中而不是读到单个缓冲区中。同样地,一个聚集写入是向缓冲区数组而不是向单个缓冲区写入数据。

分散/聚集 I/O 对于将数据流划分为单独的部分很有用,这有助于实现复杂的数据格式。

6.2  分散/聚集 I/O

通道可以有选择地实现两个新的接口: ScatteringByteChannel 和 GatheringByteChannel。一个 ScatteringByteChannel 是一个具有两个附加读方法的通道:

long read( ByteBuffer[] dsts );
long read( ByteBuffer[] dsts, int offset, int length );

这些 long read()方法很像标准的 read 方法,只不过它们不是取单个缓冲区而是取一个缓冲区数组。

在分散读取中,通道依次填充每个缓冲区。填满一个缓冲区后,它就开始填充下一个。在某种意义上,缓冲区数组就像一个大缓冲区。

6.3  分散/聚集的应用

分散/聚集 I/O 对于将数据划分为几个部分很有用。例如,您可能在编写一个使用消息对象的网络应用程序,每一个消息被划分为固定长度的头部和固定长度的正文。您可以创建一个刚好可以容纳头部的缓冲区和另一个刚好可以容难正文的缓冲区。当您将它们放入一个数组中并使用分散读取来向它们读入消息时,头部和正文将整齐地划分到这两个缓冲区中。

我们从缓冲区所得到的方便性对于缓冲区数组同样有效。因为每一个缓冲区都跟踪自己还可以接受多少数据,所以分散读取会自动找到有空间接受数据的第一个缓冲区。在这个缓冲区填满后,它就会移动到下一个缓冲区。

6.4  聚集写入

聚集写入类似于分散读取,只不过是用来写入。它也有接受缓冲区数组的方法:

long write( ByteBuffer[] srcs );
long write( ByteBuffer[] srcs, int offset, int length );

聚集写对于把一组单独的缓冲区中组成单个数据流很有用。为了与上面的消息例子保持一致,您可以使用聚集写入来自动将网络消息的各个部分组装为单个数据流,以便跨越网络传输消息。

从例子程序UseScatterGather.java 中可以看到分散读取和聚集写入的实际应用。

// UseScatterGather
import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
public class UseScatterGather
{
  static private final int firstHeaderLength = 2;
  static private final int secondHeaderLength = 4;
  static private final int bodyLength = 6;
  static public void main( String args[] ) throws Exception {
    if (args.length!=1) {
      System.err.println( "Usage: java UseScatterGather port" );
      System.exit( 1 );
    }
    int port = Integer.parseInt( args[0] );
    ServerSocketChannel ssc = ServerSocketChannel.open();
    InetSocketAddress address = new InetSocketAddress( port );
    ssc.socket().bind( address );
    int messageLength =
      firstHeaderLength + secondHeaderLength + bodyLength;
    ByteBuffer buffers[] = new ByteBuffer[3];
    buffers[0] = ByteBuffer.allocate( firstHeaderLength );
    buffers[1] = ByteBuffer.allocate( secondHeaderLength );
    buffers[2] = ByteBuffer.allocate( bodyLength );
    SocketChannel sc = ssc.accept();
    while (true) {
      // Scatter-read into buffers
      int bytesRead = 0;
      while (bytesRead < messageLength) {
        long r = sc.read( buffers );
        bytesRead += r;
        System.out.println( "r "+r );
        for (int i=0; i<buffers.length; ++i) {
          ByteBuffer bb = buffers[i];
          System.out.println( "b "+i+" "+bb.position()+" "+bb.limit() );
        }
      }
      // Process message here
      // Flip buffers
      for (int i=0; i<buffers.length; ++i) {
        ByteBuffer bb = buffers[i];
        bb.flip();
      }
      // Scatter-write back out
      long bytesWritten = 0;
      while (bytesWritten<messageLength) {
        long r = sc.write( buffers );
        bytesWritten += r;
      }
      // Clear buffers
      for (int i=0; i<buffers.length; ++i) {
        ByteBuffer bb = buffers[i];
        bb.clear();
      }
      System.out.println( bytesRead+" "+bytesWritten+" "+messageLength );
    }
  }
}

NIO入门系列之第6章:分散和聚集

时间: 2024-10-12 05:03:45

NIO入门系列之第6章:分散和聚集的相关文章

NIO入门系列之第5章:关于缓冲区的更多内容

第5章 关于缓冲区的更多内容 5.1  概述 到目前为止,您已经学习了使用缓冲区进行日常工作所需要掌握的大部分内容.我们的例子没怎么超出标准的读/写过程种类,在原来的 I/O中可以像在 NIO 中一样容易地实现这样的标准读写过程. 本节将讨论使用缓冲区的一些更复杂的方面,比如缓冲区分配.包装和分片.我们还会讨论 NIO 带给 Java 平台的一些新功能.您将学到如何创建不同类型的缓冲区以达到不同的目的,如可保护数据不被修改的只读缓冲区,和直接映射到底层操作系统缓冲区的直接缓冲区.我们将在本节的最

NIO入门系列之第7章:文件锁定

第7章 文件锁定 7.1  概述 文件锁定初看起来可能让人迷惑.它似乎指的是防止程序或者用户访问特定文件.事实上,文件锁就像常规的 Java 对象锁-它们是劝告式的(advisory)锁.它们不阻止任何形式的数据访问,相反,它们通过锁的共享和获取赖允许系统的不同部分相互协调. 您可以锁定整个文件或者文件的一部分.如果您获取一个排它锁,那么其他人就不能获得同一个文件或者文件的一部分上的锁.如果您获得一个共享锁,那么其他人可以获得同一个文件或者文件一部分上的共享锁,但是不能获得排它锁.文件锁定并不总

NIO入门系列之第4章:缓冲区内部细节

4.1  概述 本节将介绍 NIO 中两个重要的缓冲区组件:状态变量和访问方法 (accessor). 状态变量是前一节中提到的"内部统计机制"的关键.每一个读/写操作都会改变缓冲区的状态.通过记录和跟踪这些变化,缓冲区就可能够内部地管理自己的资源. 在从通道读取数据时,数据被放入到缓冲区.在有些情况下,可以将这个缓冲区直接写入另一个通道,但是在一般情况下,您还需要查看数据.这时使用访问方法 get() 来完成的.同样,如果要将原始数据放入缓冲区中,就要使用访问方法 put(). 在本

NIO入门系列之第3章:从理论到实践:NIO 中的读和写

3.1  概述 读和写是 I/O 的基本过程.从一个通道中读取很简单:只需创建一个缓冲区,然后让通道将数据读到这个缓冲区中.写入也相当简单:创建一个缓冲区,用数据填充它,然后让通道用这些数据来执行写入操作. 在本节中,我们将学习有关在Java 程序中读取和写入数据的一些知识.我们将回顾 NIO 的主要组件(缓冲区.通道和一些相关的方法),看看它们是如何交互以进行读写的.在接下来的几节中,我们将更详细地分析这其中的每个组件以及其交互. 3.2  从文件中读取 在我们第一个练习中,我们将从一个文件中

NIO入门系列之第8章:连网和异步 I/O

8.1  概述 连网是学习异步 I/O 的很好基础,而异步 I/O 对于在 Java 语言中执行任何输入/输出过程的人来说,无疑都是必须具备的知识.NIO 中的连网与 NIO 中的其他任何操作没有什么不同--它依赖通道和缓冲区,而您通常使用InputStream 和 OutputStream来获得通道. 本节首先介绍异步 I/O 的基础-它是什么以及它不是什么,然后转向更实用的.程序性的例子. 8.2  异步 I/O 异步 I/O 是一种没有阻塞地读写数据的方法.通常,在代码进行 read()

NIO入门系列之第9章:字符集

9.1  概述 根据 Sun 的文档,一个 Charset 是"十六位 Unicode 字符序列与字节序列之间的一个命名的映射".实际上,一个 Charset 允许您以尽可能最具可移植性的方式读写字符序列. Java 语言被定义为基于 Unicode.然而在实际上,许多人编写代码时都假设一个字符在磁盘上或者在网络流中用一个字节表示.这种假设在许多情况下成立,但是并不是在所有情况下都成立,而且随着计算机变得对 Unicode 越来越友好,这个假设就日益变得不能成立了. 在本节中,我们将看

NIO入门系列之第一章:输入/输出:概念性描述

第1章 输入/输出:概念性描述 1.1  I/O 简介 I/O 或者输入/输出指的是计算机与外部世界或者一个程序与计算机的其余部分的之间的接口.它对于任何计算机系统都非常关键,因而所有 I/O 的主体实际上是内置在操作系统中的.单独的程序一般是让系统为它们完成大部分的工作. 在 Java 编程中,直到最近一直使用流的方式完成 I/O.所有 I/O 都被视为单个的字节的移动,通过一个称为 Stream 的对象一次移动一个字节.流 I/O 用于与外部世界接触.它也在内部使用,用于将对象转换为字节,然

NIO入门系列之第二章:通道和缓冲区

第2章 通道和缓冲区 2.1  概述 通道和缓冲区是 NIO 中的核心对象,几乎在每一个I/O 操作中都要使用它们. 通道是对原 I/O 包中的流的模拟.到任何目的地(或来自任何地方)的所有数据都必须通过一个 Channel 对象.一个 Buffer 实质上是一个容器对象.发送给一个通道的所有对象都必须首先放到缓冲区中:同样地,从通道中读取的任何数据都要读到缓冲区中. 2.2  什么是缓冲区? Buffer 是一个对象,它包含一些要写入或者刚读出的数据.在 NIO 中加入 Buffer 对象,体

Jenkins入门系列之——02第二章 Jenkins安装与配置

2014-12-08:已不再担任SCM和CI的职位,Jenkins的文章如无必要不会再维护. 写的我想吐血,累死了. 网页看着不爽的,自己去下载PDF.有问题请留言! Jenkins入门系列之--03PDF文档下载 第二章 Jenkins安装与配置 2 Jenkins安装 在最简单的情况下,Jenkins 只需要两个步骤: 1.下载最新的版本(一个 WAR 文件).Jenkins官方网址: http://Jenkins-ci.org/ 2.运行 java -jar jenkins.war 注意: