通道是由java.nio.channels包定义的,表示IO源与目标打开的连接。Channel类似于传统的“流”,只不过Channel本身不能直接访问数据,只能与Buffer进行交互。
通道是java.nio全新的东西,不是扩展也不是增强。通道可以理解为管道,比如要将一个大桶中的水排出,可以在桶壁上插上一个管道,使得水从管道中流出来,水就可以看成是数据。
通道的类型
FileChannel:用于读取、写入、映射和操作本地文件的通道
DatagramChannel:通过UDP读写网络中的数据通道
SocketChannel:通过TCP读写网络中的数据
ServerSocketChannel:可以监听新进来的TCP连接,对每一个新进来的连接都会创建一个SocketChannel
获取通道的方式:
一是对支持通道的对象调用getChannel()方法。
支持通道的类:
本地IO:FileInputStream、FileOutputStream、RandomAccessFile
网络IO:DatagramSocket、Socket、ServerSocket
二是在JDK1.7中的NIO.2针对各个通道提供了静态方法open()
三是在JDK1.7中的NIO.2的Files工具类的newByteChannel()方法
测试Channel
下面的代码是利用Channel来复制文件,首先采用的是非直接缓冲区方式,非直接缓冲区操作起来比较慢,它是直接通过普通的方式获取缓冲区,默认是非直接缓冲区:
inputStream = new FileInputStream("d:/1.iso");
outputStream = new FileOutputStream("d:/2.iso");
channel1 = inputStream.getChannel();
channel2 = outputStream.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
while(channel1.read(buffer) != -1){
buffer.flip();
channel2.write(buffer);
buffer.clear();
}
下面的代码是采用直接缓冲区的方式来进行,直接缓冲区可以使用Channel的map方法来创建,这里面的Channel是采取open方法来创建的:
channel1 = FileChannel.open(Paths.get("d:/1.mp3"), StandardOpenOption.READ);
channel2 = FileChannel.open(Paths.get("d:/2.mp3"), StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
buffer1 = channel1.map(FileChannel.MapMode.READ_ONLY,0,channel1.size());
buffer2 = channel2.map(FileChannel.MapMode.READ_WRITE,0,channel1.size());
byte[] bytes = new byte[buffer1.limit()];
buffer1.get(bytes);
buffer2.put(bytes);
open方法创建Channel时,可以设置是创建什么类型的通道,StandardOpenOption类定义的是打开类型,可以是READ,WRITE等等各种类型。同样的,在使用map方法创建缓冲区时也需要设定模式,例如对应channel1,创建buffer1就可以使用只读READ_ONLY模式;但是对于channel2,因为是通过channel2写入2.mp3,所以既可读又可写,所以在创建缓冲区的时候需要使用读写READ_WRITE模式。
通道和缓冲区数据传输
数据是从Buffer中写入Channel
int bytes = channel.write(buffer);
数据是从Channel中读取到Buffer中的
int bytes = channel.write(buffer);
通道和通道之间的数据传输
transferFrom():该方法是从数据源通道将数据传输到目的通道,参数是源通道和源通道大小
channel2.transferFrom(channel1,0,channel1.size());//channel2为目的通道,channel1为源通道
transferTo():发方法是从数据源通道将数据传输到目的通道,参数是源通道大小和目的通道
channel1.transferTo(0,channel1.size(),channel2);//channel1为源通道,channel2为目的通道
分散读取和聚集写入
分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中。分散读取时,需要按照Buffer的顺序,从Channel中读取数据依次将Buffer填满。
聚集写入(Gathering Writes):将多个缓冲区中的数据聚集到通道中。聚集写入时,需要按照Buffer的顺序,写入position和limit之间的数据到Channel。
@Test
//测试分散读取和聚集写入
public void testScatterAndGather(){
RandomAccessFile file = null;
RandomAccessFile file2 = null;
try {
file = new RandomAccessFile("d:/1.txt","rw");
FileChannel channel = file.getChannel();
ByteBuffer buffer1 = ByteBuffer.allocate(20);
ByteBuffer buffer2 = ByteBuffer.allocate(50);
ByteBuffer[] buffers = {buffer1,buffer2};
channel.read(buffers);
for(ByteBuffer byteBuffer : buffers){
byteBuffer.flip();
}
System.out.println(new String(buffers[0].array(), 0, buffers[0].limit()));
System.out.println("\n");
System.out.println(new String(buffers[1].array(), 0, buffers[1].limit()));
file2 = new RandomAccessFile("d:/2.txt","rw");
FileChannel channel1 = file2.getChannel();
channel1.write(buffers);
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}finally {
try {
file.close();
file2.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}