Java NIO(三)通道
概念
通道(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(三)通道的更多相关文章
- Java NIO Channel通道
		
原文链接:http://tutorials.jenkov.com/java-nio/channels.html Java NIO Channel通道和流非常相似,主要有以下几点区别: 通道可以读也可以 ...
 - JAVA NIO Socket通道
		
DatagramChannel和SocketChannel都实现定义读写功能,ServerSocketChannel不实现,只负责监听传入的连接,并建立新的SocketChannel,本身不传输数 ...
 - Java NIO之通道
		
一.前言 前面学习了缓冲区的相关知识点,接下来学习通道. 二.通道 2.1 层次结构图 对于通道的类层次结构如下图所示. 其中,Channel是所有类的父类,其定义了通道的基本操作.从 Channel ...
 - Java IO和Java NIO 和通道 在文件拷贝上的性能差异分析
		
1. 在JAVA传统的IO系统中,读取磁盘文件数据的过程如下: 以FileInputStream类为例,该类有一个read(byte b[])方法,byte b[]是我们要存储读取到用户空间的缓冲区 ...
 - 【NIO】Java NIO之通道
		
一.前言 前面学习了缓冲区的相关知识点,接下来学习通道. 二.通道 2.1 层次结构图 对于通道的类层次结构如下图所示. 其中,Channel是所有类的父类,其定义了通道的基本操作.从 Channel ...
 - Java NIO:通道
		
最近打算把Java网络编程相关的知识深入一下(IO.NIO.Socket编程.Netty) Java NIO主要需要理解缓冲区.通道.选择器三个核心概念,作为对Java I/O的补充, 以提升大批量数 ...
 - Java NIO 文件通道 FileChannel 用法
		
FileChannel 提供了一种通过通道来访问文件的方式,它可以通过带参数 position(int) 方法定位到文件的任意位置开始进行操作,还能够将文件映射到直接内存,提高大文件的访问效率.本文将 ...
 - Java NIO之通道Channel
		
channel与流的区别: 流基于字节,且读写为单向的. 通道基于快Buffer,可以异步读写.除了FileChannel之外都是双向的. channel的主要实现: FileChannel Data ...
 - Java NIO 文件通道使用
		
读取一个文件的内容,然后写入另外一个文件 public class NioTest4 { public static void main(String[] args) throws Exception ...
 - Java NIO系列教程(二) Channel通道介绍及FileChannel详解
		
目录: <Java NIO系列教程(二) Channel> <Java NIO系列教程(三) Channel之Socket通道> Channel是一个通道,可以通过它读取和写入 ...
 
随机推荐
- ObjectT5:在线随机森林-Multi-Forest-A chameleon in track in
			
原文::Multi-Forest:A chameleon in tracking,CVPR2014 下的蛋...原文 使用随机森林的优势,在于可以使用GPU把每棵树分到一个流处理器里运行,容易并行化 ...
 - socket 的通信过程
			
1.建立套接字 Linux在利用socket()系统调用建立新的套接字时,需要传递套接字的地址族标识符.套接字类型以及协议,其函数定义于net/socket.c中: asmlinkage long s ...
 - codeforce 788 A. Funtions again
			
链接 A. Functions again 题意 这是一道求最大连续子序列和变形题. 做法 先将abs(a[i+1]-a[i]算出来,然后用两个数组dp[i],cp[i],dp维护其最大值,cp维护其 ...
 - python调用函数实现数据的增删改查(1)
			
实现一个小功能,当输入相应的序号,会执行相关操作, 比如当输入序号1,会执行添加功能 #coding:utf-8print '''1 添加数据2 删除数据3 修改数据4 查看数据5 退出程序'''de ...
 - javaee 文件的读取
			
package Shurushucu; import java.io.FileNotFoundException; import java.io.FileOutputStream; import ja ...
 - sh脚本文件
			
echo `date +%F` 打印当前日期 用于写备份脚本按照名字命名
 - BZOJ 3110 [ZJOI2013]K大数查询 (整体二分+线段树)
			
和dynamic rankings这道题的思想一样 只不过是把树状数组换成线段树区间修改,求第$K$大的而不是第$K$小的 这道题还有负数,需要离散 #include <vector> # ...
 - python_格式化拼接、format,编码、解码
			
一.格式化拼接.format 1.字符串拼接 name = "Monica", age = 16 print("姓名"+name+“年龄”+age+" ...
 - Tensorflow原理通用
			
使用 TensorFlow, 你必须明白 TensorFlow: 使用图 (graph) 来表示计算任务.在被称之为 会话 (Session) 的上下文 (context) 中执行图.使用 tenso ...
 - HelloWorld编译正常运行报noclassdeffounderror
			
修改环境变量classpath: 原理: classpath是搜索java类库的路径:当你输入命令“java HelloWorld“时,会根据classpath寻找HelloWorld.class:一 ...