1. 程式人生 > 其它 >NIO核心之Channel通道

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的載體 自己本身不具備傳輸資訊的能力