NIO--通道(Channel)

通道(Channel)

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

通道可以用來讀取和寫入數據,通道類似於之前的輸入/輸出流,但是程序不會直接操作通道的,所有的內容都是先讀到或寫入到緩衝區中,再通過緩衝區中取得獲寫入的。

通道與傳統的流操作不同,傳統的流操作是分爲輸入或輸出流的,而通道本身是雙向操作的,即可以完成輸入也可以完成輸出。

在這裏插入圖片描述
在這裏插入圖片描述

Java 爲 Channel 接口提供的最主要實現類如下

  • FileChannel:用於讀取、寫入、映射和操作文件的通道。
  • DatagramChannel:通過 UDP 讀寫網絡中的數據通道。
  • SocketChannel:通過 TCP 讀寫網絡中的數據。
  • ServerSocketChannel:可以監聽新進來的 TCP 連接,對每一個新進來 的連接都會創建一個 SocketChannel。

獲取通道

獲取通道的一種方式是對支持通道的對象調用 getChannel() 方法。支持通道的類如下:

  • FileInputStream
  • FileOutputStream
  • RandomAccessFile
  • DatagramSocket
  • Socket
  • ServerSocket

獲取通道的其他方式是使用 Files 類的靜態方法 newByteChannel() 獲 取字節通道。或者通過通道的靜態方法 open() 打開並返回指定通道。

通道的數據傳輸

  • 將 Buffer 中數據寫入 Channel 例如:
//將Buffer中數據寫入Channel中
int bytesWritten = inChannel.write(buf);
  • 從 Channel 讀取數據到 Buffer 例如:
//從Channel讀取數據到Buffer中
int bytesRead = inChannel.read(buf);

分散(Scatter)和聚集(Gather)

分散讀取(Scattering Reads)是指從 Channel 中讀取的數據“分 散”到多個 Buffer 中。
在這裏插入圖片描述
注意:按照緩衝區的順序,從 Channel 中讀取的數據依次將 Buffer 填滿。

聚集寫入(Gathering Writes)是指將多個 Buffer 中的數據“聚集” 到 Channel。
在這裏插入圖片描述

注意:按照緩衝區的順序,寫入 position 和 limit 之間的數據到 Channel 。

transferFrom()

將數據從源通道傳輸到其他 Channel 中:

在這裏插入圖片描述

transferTo()

將數據從源通道傳輸到其他 Channel 中:
在這裏插入圖片描述

Channel接口定義的方法

在這裏插入圖片描述

FileChannel的常用方法

FileChannel是Channel的子類,可以進行文件的讀寫操作。

在這裏插入圖片描述

測試

package com.atguigu.nio;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.junit.Test;

/*
 * 一、通道(Channel):用於源節點與目標節點的連接。在 Java NIO 中負責緩衝區中數據的傳輸。Channel 本身不存儲數據,因此需要配合緩衝區進行傳輸。
 * 
 * 二、通道的主要實現類
 * 	java.nio.channels.Channel 接口:
 * 		|--FileChannel
 * 		|--SocketChannel
 * 		|--ServerSocketChannel
 * 		|--DatagramChannel
 * 
 * 三、獲取通道
 * 1. Java 針對支持通道的類提供了 getChannel() 方法
 * 		本地 IO:
 * 		FileInputStream/FileOutputStream
 * 		RandomAccessFile
 * 
 * 		網絡IO:
 * 		Socket
 * 		ServerSocket
 * 		DatagramSocket
 * 		
 * 2. 在 JDK 1.7 中的 NIO.2 針對各個通道提供了靜態方法 open()
 * 3. 在 JDK 1.7 中的 NIO.2 的 Files 工具類的 newByteChannel()
 * 
 * 四、通道之間的數據傳輸
 * transferFrom()
 * transferTo()
 * 
 * 五、分散(Scatter)與聚集(Gather)
 * 分散讀取(Scattering Reads):將通道中的數據分散到多個緩衝區中
 * 聚集寫入(Gathering Writes):將多個緩衝區中的數據聚集到通道中
 * 
 * 六、字符集:Charset
 * 編碼:字符串 -> 字節數組
 * 解碼:字節數組  -> 字符串
 * 
 */
public class TestChannel {
	
	//字符集
	@Test
	public void test6() throws IOException{
		Charset cs1 = Charset.forName("GBK");
		
		//獲取編碼器
		CharsetEncoder ce = cs1.newEncoder();
		
		//獲取解碼器
		CharsetDecoder cd = cs1.newDecoder();
		
		CharBuffer cBuf = CharBuffer.allocate(1024);
		cBuf.put("尚硅谷威武!");
		cBuf.flip();
		
		//編碼
		ByteBuffer bBuf = ce.encode(cBuf);
		
		for (int i = 0; i < 12; i++) {
			System.out.println(bBuf.get());
		}
		
		//解碼
		bBuf.flip();
		CharBuffer cBuf2 = cd.decode(bBuf);
		System.out.println(cBuf2.toString());
		
		System.out.println("------------------------------------------------------");
		
		Charset cs2 = Charset.forName("GBK");
		bBuf.flip();
		CharBuffer cBuf3 = cs2.decode(bBuf);
		System.out.println(cBuf3.toString());
	}
	
	@Test
	public void test5(){
		Map<String, Charset> map = Charset.availableCharsets();
		
		Set<Entry<String, Charset>> set = map.entrySet();
		
		for (Entry<String, Charset> entry : set) {
			System.out.println(entry.getKey() + "=" + entry.getValue());
		}
	}
	
	//分散和聚集
	@Test
	public void test4() 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);
	}
	
	//通道之間的數據傳輸(直接緩衝區)
	@Test
	public void test3() throws IOException{
		FileChannel inChannel = FileChannel.open(Paths.get("d:/1.mkv"), StandardOpenOption.READ);
		FileChannel outChannel = FileChannel.open(Paths.get("d:/2.mkv"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);
		
//		inChannel.transferTo(0, inChannel.size(), outChannel);
		outChannel.transferFrom(inChannel, 0, inChannel.size());
		
		inChannel.close();
		outChannel.close();
	}
	
	//使用直接緩衝區完成文件的複製(內存映射文件)
	@Test
	public void test2() throws IOException{//2127-1902-1777
		long start = System.currentTimeMillis();
		
		FileChannel inChannel = FileChannel.open(Paths.get("d:/1.mkv"), StandardOpenOption.READ);
		FileChannel outChannel = FileChannel.open(Paths.get("d:/2.mkv"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);
		
		//內存映射文件
		MappedByteBuffer inMappedBuf = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());
		MappedByteBuffer outMappedBuf = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());
		
		//直接對緩衝區進行數據的讀寫操作
		byte[] dst = new byte[inMappedBuf.limit()];
		inMappedBuf.get(dst);
		outMappedBuf.put(dst);
		
		inChannel.close();
		outChannel.close();
		
		long end = System.currentTimeMillis();
		System.out.println("耗費時間爲:" + (end - start));
	}
	
	//利用通道完成文件的複製(非直接緩衝區)
	@Test
	public void test1(){//10874-10953
		long start = System.currentTimeMillis();
		
		FileInputStream fis = null;
		FileOutputStream fos = null;
		//①獲取通道
		FileChannel inChannel = null;
		FileChannel outChannel = null;
		try {
			fis = new FileInputStream("d:/1.mkv");
			fos = new FileOutputStream("d:/2.mkv");
			
			inChannel = fis.getChannel();
			outChannel = fos.getChannel();
			
			//②分配指定大小的緩衝區
			ByteBuffer buf = ByteBuffer.allocate(1024);
			
			//③將通道中的數據存入緩衝區中
			while(inChannel.read(buf) != -1){
				buf.flip(); //切換讀取數據的模式
				//④將緩衝區中的數據寫入通道中
				outChannel.write(buf);
				buf.clear(); //清空緩衝區
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if(outChannel != null){
				try {
					outChannel.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			
			if(inChannel != null){
				try {
					inChannel.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			
			if(fos != null){
				try {
					fos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			
			if(fis != null){
				try {
					fis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		
		long end = System.currentTimeMillis();
		System.out.println("耗費時間爲:" + (end - start));
		
	}

}
發佈了774 篇原創文章 · 獲贊 2142 · 訪問量 27萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章