概念

通道(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. Absolute Horizontal And Vertical Centering In CSS

    Quick CSS Trick: How To Center an Object Exactly In The Center Centering in CSS: A Complete Guide Ab ...

  2. java学习笔记4——返回值

    这个简单,返回值就是计算结果. 打个比方:个表格中我只要结果,不要经过,这个返回值就是结果.这个过程就是函数. 另外还有一个函数套用一个函数,被套用的函数的结果作为一个返回值给套用的外层函使用.比如: ...

  3. sql_2

    编辑表结构ALTER TABLE `sp_account_trans`     MODIFY COLUMN `TRANS_DESC` varchar(81) CHARACTER SET utf8 CO ...

  4. MySQL_pymysql模块

    安装:pip install pymysql 基本操作 import pymysql conn=pymysql.connect(host=',database='lary',charset='utf8 ...

  5. The socket is closed!

    关闭mongodb    /usr/local/app/mongidb//bin/mongod   --shutdown  --dbpath /usr/local/data/mongo/data/ 然 ...

  6. redis_ 5 集群

    [转自 ]https://www.cnblogs.com/hjwublog/p/5681700.html#_label0 Redis集群简介 Redis 集群是3.0之后才引入的,在3.0之前,使用哨 ...

  7. mybatis入门截图三

    ----------------------------------------------- <!-- 用户信息的综合查询 --> <select id="userlis ...

  8. 全球级的分布式数据库 Google Spanner原理

    开发四年只会写业务代码,分布式高并发都不会还做程序员?->>>    Google Spanner简介 Spanner 是Google的全球级的分布式数据库 (Globally-Di ...

  9. 从零開始写游戏引擎(一) - project创建以及文件夹设置还有版本号控制

    一句话提要 好的開始等于成功了一半. 创建文件夹结构 project文件夹下最好分为以下几个文件夹 Docs - 开发文档,设计文档 Assets - 角色,动作,模型和音效等 Source - 代码 ...

  10. c/c++中sleep()函数毫秒级的实现

    近期看到好多人在问.c/c++中的sleep函数是秒级的,能不能实现毫秒级的呢?当然非常easy.我的写法例如以下 #include <stdio.h> #include <sys/ ...