NIO核心之Channel通道

Posted ChiHsien

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NIO核心之Channel通道相关的知识,希望对你有一定的参考价值。

通道Channe概述

通道(Channel):由 java.nio.channels 包定义 的。Channel 表示 IO 源与目标打开的连接。 Channel 类似于传统的“流”。只不过 Channel 本身不能直接访问数据,Channel 只能与 Buffer 进行交互,本质上是Buffer的载体。

1、 NIO 的通道类似于流,但有些区别如下:

  • 通道可以同时进行读写,而流只能读或者只能写

  • 通道可以实现异步读写数据

  • 通道可以从缓冲读数据,也可以写数据到缓冲:

2、BIO 中的 stream 是单向的,例如 FileInputStream 对象只能进行读取数据的操作,而 NIO 中的通道(Channel) 是双向的,可以读操作,也可以写操作。

3、Channel 在 NIO 中是一个接口

public interface Channel extends Closeable{}

常用的Channel实现类

  • FileChannel:用于读取、写入、映射和操作的文件通道。

  • DatagramChannel:通过 UDP 读写网络中的数据通道。

  • SocketChannel:通过 TCP 读写网络中的数据。

  • ServerSocketChannel:可以监听新进来的 TCP 连接,对每一个新进来的连接都会创建一个 SocketChannel。 【ServerSocketChanne 类似 ServerSocket , SocketChannel 类似 Socket】

FileChannel 类

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

  • FileInputStream

  • FileOutputStream

  • RandomAccessFile

  • DatagramSocket

  • Socket

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

FileChannel的常用方法

int read(ByteBuffer dst) 从 从  Channel 到 中读取数据到  ByteBuffer
long  read(ByteBuffer[] dsts) 将 将  Channel 到 中的数据“分散”到  ByteBuffer[]
int  write(ByteBuffer src) 将 将  ByteBuffer 到 中的数据写入到  Channel
long write(ByteBuffer[] srcs) 将 将  ByteBuffer[] 到 中的数据“聚集”到  Channel
long position() 返回此通道的文件位置
FileChannel position(long p) 设置此通道的文件位置
long size() 返回此通道的文件的当前大小
FileChannel truncate(long s) 将此通道的文件截取为给定大小
void force(boolean metaData) 强制将所有对此通道的文件更新写入到存储设备中

案例1-本地文件写数据

需求:使用前面学习后的 ByteBuffer(缓冲) 和 FileChannel(通道), 将 "hello,JavaCoder!" 写入到 data.txt 中。

package com.itheima;


import org.junit.Test;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class ChannelTest {
    @Test
    public void write(){
        try {
            // 1、字节输出流通向目标文件
            FileOutputStream fos = new FileOutputStream("data01.txt");
            // 2、得到字节输出流对应的通道Channel
            FileChannel channel = fos.getChannel();
            // 3、分配缓冲区
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            buffer.put("hello,chihsien!".getBytes());
            // 4、把缓冲区切换成写出模式
            buffer.flip();
            channel.write(buffer);
            channel.close();
            System.out.println("写数据到文件中成功!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

案例2-本地文件读数据

需求:使用前面学习后的 ByteBuffer(缓冲) 和 FileChannel(通道), 将 data01.txt 中的数据读入到程序,并显示在控制台屏幕

public class ChannelTest {

    @Test
    public void read() throws Exception {
        // 1、定义一个文件字节输入流与源文件接通
        FileInputStream is = new FileInputStream("data01.txt");
        // 2、需要得到文件字节输入流的文件通道
        FileChannel channel = is.getChannel();
        // 3、定义一个字节缓冲区并给定大小
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        // 4、读取数据到缓冲区
        channel.read(buffer);
        //归零指针位置准备读取数据
        buffer.flip();
        // 5、读取出缓冲区中的数据并输出即可 从第一个位置读到剩下可读的位置
        String rs = new String(buffer.array(),0,buffer.remaining());
        System.out.println(rs);

    }

案例3-使用Buffer完成文件复制

使用 FileChannel(通道) ,完成文件的拷贝。

@Test
public void copy() throws Exception {
    // 源文件
    File srcFile = new File("E:\\Codes-Review\\\\文件\\\\壁纸.jpg");
    File destFile = new File("E:\\Codes-Review\\\\文件\\\\壁纸new.jpg");
    // 得到一个字节字节输入流
    FileInputStream fis = new FileInputStream(srcFile);
    // 得到一个字节输出流
    FileOutputStream fos = new FileOutputStream(destFile);
    // 得到的是文件通道
    FileChannel isChannel = fis.getChannel();
    FileChannel osChannel = fos.getChannel();
    // 分配缓冲区
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    while(true){
        // 每次读写之前必须先清空缓冲然后再写入数据到缓冲区
        buffer.clear();
        // 开始读取一次数据
        int flag = isChannel.read(buffer);
        // -1 说明数据没有了 直接停止循环
        if(flag == -1){
            break;
        }
        // 已经读取了数据 ,把缓冲区的模式切换成可读模式
        buffer.flip();
        // 把数据写出到
        osChannel.write(buffer);
    }
    //关闭通道
    isChannel.close();
    osChannel.close();
    System.out.println("复制完成!");
}

案例4-分散 (Scatter) 和聚集 (Gather)

 

 聚集写入(Gathering )是指将多个 Buffer 中的数据“聚集”到 Channel。

 

 

//分散和聚集
@Test
public void test() throws IOException{
        RandomAccessFile raf1 = new RandomAccessFile("1.txt", "rw");
    //1. 获取通道
    FileChannel channel1 = raf1.getChannel();
    
    //2. 分配指定大小的缓冲区
    ByteBuffer buf1 = ByteBuffer.allocate(100);
    ByteBuffer buf2 = ByteBuffer.allocate(1024);
    
    //3. 分散读取
    ByteBuffer[] bufs = {buf1, buf2};
    channel1.read(bufs);
    
    for (ByteBuffer byteBuffer : bufs) {
        byteBuffer.flip();
    }
    
    System.out.println(new String(bufs[0].array(), 0, bufs[0].limit()));
    System.out.println("-----------------");
    System.out.println(new String(bufs[1].array(), 0, bufs[1].limit()));
    
    //4. 聚集写入
    RandomAccessFile raf2 = new RandomAccessFile("2.txt", "rw");
    FileChannel channel2 = raf2.getChannel();
    
    channel2.write(bufs);
}

案例5-transferFrom()

从目标通道中去复制原通道数据

@Test
public void test02() throws Exception {
    // 1、字节输入管道
    FileInputStream is = new FileInputStream("data01.txt");
    FileChannel isChannel = is.getChannel();
    // 2、字节输出流管道
    FileOutputStream fos = new FileOutputStream("data03.txt");
    FileChannel osChannel = fos.getChannel();
    // 3、复制
    osChannel.transferFrom(isChannel,isChannel.position(),isChannel.size());
    isChannel.close();
    osChannel.close();
}

案例6-transferTo()

把原通道数据复制到目标通道

@Test
public void test02() throws Exception {
    // 1、字节输入管道
    FileInputStream is = new FileInputStream("data01.txt");
    FileChannel isChannel = is.getChannel();
    // 2、字节输出流管道
    FileOutputStream fos = new FileOutputStream("data04.txt");
    FileChannel osChannel = fos.getChannel();
    // 3、复制
    isChannel.transferTo(isChannel.position() , isChannel.size() , osChannel);
    isChannel.close();
    osChannel.close();
}

通道本质上是buffer的载体 自己本身不具备传输信息的能力

 

以上是关于NIO核心之Channel通道的主要内容,如果未能解决你的问题,请参考以下文章

Java NIO之通道

Java NIO之通道

Java NIO 之 Channel(通道)

Java NIO 之 Channel(通道)

java的nio之:java的nio系列教程之channel的数据交换

java nio之 channel通道