概念

通道(Channel)由java.nio.channels包定义的。channel表示IO源与目标打开的连接,类似流,但不能直接访问数据,只能与Buffer进行交互

通道类似流,但又有不同:

  • 既可以从通道中读取数据,又可以将数据写入通道,而流的读写通常是单向的
  • 通道可以异步的读写
  • 通道中的数据总要先读到一个Buffer中,或从一个Buffer中写入通道

通常情况下,通道与操作系统的文件描述符(FileDescriptor)和文件句柄(FileHandler)有着一对一的关系。虽然通道比文件描述符更广义,但开发者经常使用到的多数通道都是连接到开放的文件描述符的。通道是一种途径,借助该途径,可以用最小的总开销来访问操作系统本身的I/O服务。缓冲区则是通道内部用来发送和接收数据的端点

Channel的源码:

public interface Channel extends Closeable {

    /**
* Tells whether or not this channel is open. </p>
*
* @return <tt>true</tt> if, and only if, this channel is open
*/
public boolean isOpen(); /**
* Closes this channel.
*
* <p> After a channel is closed, any further attempt to invoke I/O
* operations upon it will cause a {@link ClosedChannelException} to be
* thrown.
*
* <p> If this channel is already closed then invoking this method has no
* effect.
*
* <p> This method may be invoked at any time. If some other thread has
* already invoked it, however, then another invocation will block until
* the first invocation is complete, after which it will return without
* effect. </p>
*
* @throws IOException If an I/O error occurs
*/
public void close() throws IOException; }

和缓冲区不同,通道API主要由接口指定。不同的操作系统上通道实现会有根本性的差异,所以通道API仅仅描述了可以做什么,因此很自然地,通道实现经常使用操作系统的本地代码,通道接口允许开发者以一种受控且可移植的方式来访问底层的I/O服务。可以从底层的Channel接口看到,对所有通道来说只有两种共同的操作:检查一个通道是否打开isOpen()和关闭一个打开的通道close(),其余所有的东西都是那些实现Channel接口以及它的子接口的类。

通道的基本接口

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{

}

通道可以是单向的也可以是多向的。一个Channel类可以实现定义了read()方法的ReadableByteChannel接口,也可以实现定义了write()方法的WritableByteChannel接口。实现其中一种为单向,只能在一个方向上传输数据,两种都实现为双向的,可以双向传输数据,如ByteChannel

Channel的实现

  • FileChannel: 从文件中读写数据
  • DatagramChannel: 能通过UDP读写网络中的数据
  • SocketChannel: 能通过TCP读写网络中的数据
  • ServerSocketChannel: 监听新进来的TCP连接,对每一个新进来的连接都会创建一个SocketChannel

获取通道

获取通道的一种方式是对支持通道的对象调用getChannel()方法。支持通道的类如下:

  • FileInputStream
  • FileOutputStream
  • RandomAccessFile
  • DatagramSocket
  • Socket
  • ServerSocket

获取通道的其他方式是使用Files(JDK1.7)类的静态方法newByteChannel()获取字节通道。或者通过通道的静态方法open()打开并返回指定通道

数据传输:

int bytesWrite = inChannel.write(buf); //将Buffer中的数据写入到Channel中
int bytesRead = inChannel.read(buf); //将数据从Channel中读取到Buffer里

分散(Scatter)和聚集(Gather)

分散读取是指从Channel中读取的数据“分散”到多个Buffer中

ByteBuffer buf = ByteBuffer.allocate(128);
ByteBuffer buf2 = ByteBuffer.allocate(1024);
ByteBuffer [] bufs = { buf, buf2 };
channel.read(bufs);

注意: read方法按照ByteBuffer在数组中的顺序将Channel中的数据依次读取到Buffer中,在移动到下一个Buffer前,必须填满当前的Buffer,这也意味着它不适用于动态消息(译者注:消息大小不固定)

聚集写入是指将多个Buffer中的数据“聚集”到一个Channel中

ByteBuffer buf = ByteBuffer.allocate(128);
ByteBuffer buf2 = ByteBuffer.allocate(1024);
ByteBuffer [] bufs = { buf, buf2};
channel.write(bufs);

注意: write方法会按照Buffer在数组中的顺序依次将数据写入到Channel中,只有position和limit之间的数据才会被写入。因此,如果一个buffer的容量为128byte,但是仅仅包含58byte的数据,那么这58byte的数据将被写入到channel中。因此与Scattering Reads相反,Gathering Writes能较好的处理动态消息。

scatter / gather经常用于需要将传输的数据分开处理的场合,例如传输一个由消息头和消息体组成的消息,你可能会将消息体和消息头分散到不同的buffer中,这样你可以方便的处理消息头和消息体。

transferFrom()

FileChannel的transferFrom()方法可以将数据从源通道传输到FileChannel中

RandomAccessFile fromFile = new RandomAccessFile("fromFile.txt", "rw");
FileChannel fromChannel = fromFile.getChannel();
RandomAccessFile toFile = new RandomAccessFile("toFile.txt", "rw");
FileChannel toChannel = toFile.getChannel();
//定义传输位置
long position = 0L;
//最多传输的字节数
long count = fromChannel.size();
//将数据从源通道传输到另一个通道
toChannel.transferFrom(position, count, fromChannel);

方法的输入参数position表示从position处开始向目标通道写入数据,count表示做多传输的字节数。如果源通道的剩余空间小于 count 个字节,则所传输的字节数要小于请求的字节数。

此外要注意,在SoketChannel的实现中,SocketChannel只会传输此刻准备好的数据(可能不足count字节)。因此,SocketChannel可能不会将请求的所有数据(count个字节)全部传输到FileChannel中。

transferTo()

FileChannel的transferTo()方法是将FileChannel里的数据传输到其他Channel中

RandomAccessFile fromFile = new RandomAccessFile("fromFile.txt", "rw");
FileChannel fromChannel = fromFile.getChannel();
RandomAccessFile toFile = new RandomAccessFile("toFile.txt", "rw");
FileChannel toChannel = toFile.getChannel();
//定义传输位置
long position = 0L;
//最多传输的字节数
long count = fromChannel.size();
//将数据从源通道传输到另一个通道
fromChannel.transferTo(position, count, toChannel);

上面所说的关于SocketChannel的问题在transferTo()方法中同样存在。SocketChannel会一直传输数据直到目标buffer被填满。

Java NIO(三)通道的更多相关文章

  1. Java NIO Channel通道

    原文链接:http://tutorials.jenkov.com/java-nio/channels.html Java NIO Channel通道和流非常相似,主要有以下几点区别: 通道可以读也可以 ...

  2. JAVA NIO Socket通道

      DatagramChannel和SocketChannel都实现定义读写功能,ServerSocketChannel不实现,只负责监听传入的连接,并建立新的SocketChannel,本身不传输数 ...

  3. Java NIO之通道

    一.前言 前面学习了缓冲区的相关知识点,接下来学习通道. 二.通道 2.1 层次结构图 对于通道的类层次结构如下图所示. 其中,Channel是所有类的父类,其定义了通道的基本操作.从 Channel ...

  4. Java IO和Java NIO 和通道 在文件拷贝上的性能差异分析

    1.  在JAVA传统的IO系统中,读取磁盘文件数据的过程如下: 以FileInputStream类为例,该类有一个read(byte b[])方法,byte b[]是我们要存储读取到用户空间的缓冲区 ...

  5. 【NIO】Java NIO之通道

    一.前言 前面学习了缓冲区的相关知识点,接下来学习通道. 二.通道 2.1 层次结构图 对于通道的类层次结构如下图所示. 其中,Channel是所有类的父类,其定义了通道的基本操作.从 Channel ...

  6. Java NIO:通道

    最近打算把Java网络编程相关的知识深入一下(IO.NIO.Socket编程.Netty) Java NIO主要需要理解缓冲区.通道.选择器三个核心概念,作为对Java I/O的补充, 以提升大批量数 ...

  7. Java NIO 文件通道 FileChannel 用法

    FileChannel 提供了一种通过通道来访问文件的方式,它可以通过带参数 position(int) 方法定位到文件的任意位置开始进行操作,还能够将文件映射到直接内存,提高大文件的访问效率.本文将 ...

  8. Java NIO之通道Channel

    channel与流的区别: 流基于字节,且读写为单向的. 通道基于快Buffer,可以异步读写.除了FileChannel之外都是双向的. channel的主要实现: FileChannel Data ...

  9. Java NIO 文件通道使用

    读取一个文件的内容,然后写入另外一个文件 public class NioTest4 { public static void main(String[] args) throws Exception ...

  10. Java NIO系列教程(二) Channel通道介绍及FileChannel详解

    目录: <Java NIO系列教程(二) Channel> <Java NIO系列教程(三) Channel之Socket通道> Channel是一个通道,可以通过它读取和写入 ...

随机推荐

  1. 三维重建:深度相机方案对比-KinectFusion的基本原理(尺度)

    算法原理请参考此文:  kinect fusion 3D重建基本算法  http://log.csdn.net/xiaohu50/article/details/51592503 三维重建为三维空间实 ...

  2. Socket 数据包顺序的问题

    今天遇到一个问题,到现在还未查明原因,记录一下,留后续跟踪. 基于Netty的Socket通讯问题,Server在向Client发送数据时,假设数据原顺序为1,2,3,4...  但到了客户端顺序可能 ...

  3. linux中errno使用(转)

    当linux中的C api函数发生异常时,一般会将errno变量(需include errno.h)赋一个整数值,不同的值表示不同的含义,可以通过查看该值推测出错的原因,在实际编程中用这一招解决了不少 ...

  4. 爬虫工具--Beautifusoup

    import requests from bs4 import BeautifulSoup s=requests.Session() r=s.get('https://www.tumblr.com/l ...

  5. [tyvj-1391]走廊泼水节 最小生成树

    做克鲁斯卡尔的时候维护一个并查集即可. #include <iostream> #include <cstdio> #include <cstring> #incl ...

  6. MySQL数据库中字段类型为tinyint,读取出来为true/false的问题

    由于MySQL中没有boolean类型,所以会用到tinyint类型来表示. 数据库一个表中有一个tinyint类型的字段,值为0或者1,如果取出来的话,0会变成false,1会变成true.

  7. React 手稿 - Component state

    Component state 实例: import React, { PureComponent } from 'react'; export default class extends PureC ...

  8. 【hdu 6351】Beautiful Now

    [链接] 我是链接,点我呀:) [题意] 你可以最多交换k次数字. 让你组成一个最大的和一个最小的数字. [题解] 直接写个bfs.求出所有状态的最小交换次数. 但是最大值和最小值分开写. 做最大值的 ...

  9. 0113针对大数据量SUM的优化-思路

    转自博客:http://bbs.csdn.net/topics/390426801?page=1 优化思路:无论如何你的结果都是要扫描全有表记录,而在456010记录中,的UserName的分布导致这 ...

  10. ASP.NET-AD开发技巧

    分享一篇很好的介绍AD属性的文章 AD图片插件 如何给AD添加图片 http://www.doc88.com/p-9542932844870.html AD过滤条件 重命名ou使用user.Renam ...