【NIO】Channel 詳解


shadowLogo

概述:

由 java.nio.channels 包定義的。
Channel 表示 IO 源與目標打開的連接
Channel 類似於傳統的“流”
只不過 Channel本身不能直接訪問數據Channel 只能與Buffer 進行交互!

接下來,本人來展示下 Channel 是 如何進行數據的傳輸 的:
channel


那么,本人來講解下 Channel常用API

常用API:

現在,本人來展示下 Channel 接口的實現類的對象的獲取手段:

獲取 Channel:

手段1: 獲取通道的一種方式是對支持通道的對象調用getChannel() 方法:

  • public FileChannel getChannel()
    支持通道的類如下
  1. 本地I/O
    FileInputStream
    FileOutputStream
    RandomAccessFile

  2. 網絡 I/O
    DatagramSocket
    Socket
    ServerSocket


手段2
使用 Files 類的靜態方法 newByteChannel()方法 獲取字節通道

  • static SeekableByteChannel newByteChannel(Path path, OpenOption... options)
    打開或創建一個文件,返回一個可尋址的字節通道存取文件。
  • static SeekableByteChannel newByteChannel(Path path, Set options, FileAttribute... attrs)
    打開或創建一個文件,返回一個可尋址的字節通道存取文件

手段3
通過 Channel 接口 的靜態方法 open()方法 打開並返回指定通道

  • static FileChannel open(Path path, OpenOption... options)

在我們獲得了 Channel 接口 的實現類的對象之后,進行信息的傳輸

數據讀寫:

  • public void write(ByteBuffer dst):
    將 Buffer 中數據寫入 Channel
  • public void read(ByteBuffer dst):
    從 Channel 讀取數據到 Buffer

判斷 可用性:

  • void close()
    關閉此通道
  • boolean isOpen()
    告訴是否這個通道是打開的

那么,在本篇博文中,本人主要講解下 Channel 接口實現類中的 FileChannel類:

FileChannel 類:

獲得對象的手段在上文中已經講解過了,本人就不講解這個類的構造方法了
(一般不會有要求通過構造方法來獲取Channel的對象)

那么,本人來展示下這個類的常用API

常用API:

  • int read(ByteBufferdst):
    從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):
    強制將所有對此通道的文件更新寫入到存儲設備中

那么,現在,本人來分別展示下使用 FileChannel 類 和 非直接緩沖區/直接緩沖區 來進行文件的復制操作

使用展示:

首先是 Channel 接口 和 非直接緩沖區 版本:

package edu.youzg.about_nio.core;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class FileCopy {

    public static void main(String[] args) throws IOException {
        FileInputStream in = new FileInputStream("plantsVSzombies.mp4");
        FileOutputStream out = new FileOutputStream("copyViewFile.mp4");
        //獲取通道
        FileChannel inChannel = in.getChannel();
        FileChannel outChannel = out.getChannel();
        //面向通道,和緩沖區來復制文件
        //分配一個非直接緩沖區
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        //讀寫文件
        while (inChannel.read(byteBuffer) != -1){
            //切換讀取模式
            byteBuffer.flip();
            //寫數據
            outChannel.write(byteBuffer);
            //清空緩沖區
            byteBuffer.clear();
        }
        //釋放資源
        in.close();
        out.close();
        inChannel.close();
        outChannel.close();
    }

}

首先,本人展示下源文件的信息:
源文件
現在,本人來展示下 生成文件 的信息:
生成文件


那么,本人再來展示下使用 FileChannel 類 和 直接緩沖區 進行文件復制:

package edu.youzg.about_nio.core;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class FileCopy {

    public static void main(String[] args) throws IOException {
        //通過文件通道的靜態方法,打開讀寫通道
        //參1:通過Paths獲取源文件的路徑
        //參2:操作模式 StandardOpenOption.READ 讀取模式
        //打開讀取文件的通道
        FileChannel in = FileChannel.open(Paths.get("copyViewFile.mp4"), StandardOpenOption.READ);
        //打開寫入的通道 模式要讀還要寫  StandardOpenOption.CREATE 意思是文件不存在就創建,如果存在就覆蓋
        //StandardOpenOption.CREATE_NEW 意思是文件不存在就創建,如果存在就報錯
        FileChannel out = FileChannel.open(Paths.get("copyViewFile2.mp4"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
        //操作內存映射文件(也就是這個緩沖區在物理內存中)
        MappedByteBuffer inByteBuffer = in.map(FileChannel.MapMode.READ_ONLY, 0, in.size());
        MappedByteBuffer outByteBuffer = out.map(FileChannel.MapMode.READ_WRITE, 0, in.size());
        //直接對緩沖區進行讀寫操作
        byte[] bytes = new byte[inByteBuffer.limit()];
        inByteBuffer.get(bytes);
        outByteBuffer.put(bytes);
        //釋放資源
        in.close();
        out.close();
    }

}

現在,本人來展示下 生成文件 的信息:
生成文件


現在,本人來介紹一下通道的轉換性質
通道的轉換性質 主要依靠如下兩個方法實現:

轉換性質 API:

  • public abstract long transferFrom(ReadableByteChannel src, long position, long count):
    將字節從給定的可讀字節通道(即:輸入通道)傳輸到這個通道的文件中
  • public abstract long transferTo(long position, long count, WritableByteChannel target):
    將字節從這通道的文件給出到可寫字節通道(即:輸出通道)

那么,現在,本人來通過這兩個方法,實現下 文件復制 操作:

使用展示:

package edu.youzg.about_nio.core;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class FileCopy {

    public static void main(String[] args) throws IOException {
        FileChannel inChannel = FileChannel.open(Paths.get("copyViewFile2.mp4"), StandardOpenOption.READ);

        FileChannel outChannel1 = FileChannel.open(Paths.get("copyViewFile3.mp4"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
        FileChannel outChannel2 = FileChannel.open(Paths.get("copyViewFile4.mp4"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
        //站在輸入通道的角度
        inChannel.transferTo(0,inChannel.size(),outChannel1);
        //站在輸出通道的角度
        outChannel2.transferFrom(inChannel,0,inChannel.size());
    }

}

現在,本人來展示下 生成文件 的信息:
生成文件生成文件那么,可以看到,文件的復制成功了!


在本篇博文的最后,本人講解下一個很重要的思想 —— 分散 (Scatter) 和 聚集 (Gather)

分散(Scatter) 和 聚集(Gather):

簡介:

所謂的分散和聚集,
就是 分散讀取聚集寫入

那么,本人現在來解釋下這兩個名詞:

  • 分散讀取(Scattering Reads)
    從 Channel 中讀取的數據“分散”到多個Buffer緩沖區中
  • 聚集寫入(Gathering Writes)
    將多個 Buffer緩沖區 中的數據“聚集”到 Channel

本人現在通過兩張圖來展示下這兩個知識點:

分散讀取(Scattering Reads):

分散讀取
(注意:按照緩沖區的順序,從Channel中讀取的數據依次將Buffer填滿)

聚集寫入(Gathering Writes):

聚集寫入
(注意:按照緩沖區的順序,寫入position和limit之間的數據到Channel)


使用展示:

那么,現在,本人來利用這兩個知識點,來實現下文件的復制操作:

package edu.youzg.about_nio.core;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class FileCopy {

    public static void main(String[] args) throws IOException {
        FileChannel inChannel = FileChannel.open(Paths.get("copyViewFile4.mp4"), StandardOpenOption.READ);

        FileChannel outChanle = FileChannel.open(Paths.get("copyViewFile5.mp4"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
        //分配多個緩沖區(緩沖區要分配得足夠)
        ByteBuffer byteBuffer1 = ByteBuffer.allocate(1024*2);
        ByteBuffer byteBuffer2 = ByteBuffer.allocate(1024*1024*20);
        //定義一個數組
        ByteBuffer[] byteBuffers={byteBuffer1,byteBuffer2};
        //分散
        inChannel.read(byteBuffers);
        //聚集
        for (ByteBuffer byteBuffer : byteBuffers) {
            byteBuffer.flip();//轉換成讀取模式
        }
        //寫出數據
        outChanle.write(byteBuffers);
        //釋放資源
        outChanle.close();
        inChannel.close();
    }

}

現在,本人來展示下生成的文件:

運行結構
可以看到,文件復制成功了!


其實對於 NIO 而言,在網絡通信過程中,
對於 Channel,我們只需要掌握其 獲取Channel,以及 BufferSelector搭配使用 的方法即可!

壞笑


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM