Java NIO (三) 通道(Channel)

通道(Channel):由 java.nio.channels 包定義的,Channel 表示 IO 源與目標打開的鏈接。Channel 相似於傳統的「流」,只不過 Channel自己不能直接訪問數據,Channel 只能與Buffer 進行交互。

 

Channel的頂層接口:

public interface Channel extends Closeable {

  public boolean isOpen();

  public void close() throws IOException;

}

其中只包含最基本的兩個方法,以下圖是從《Java NIO》截取的Channel繼承樹。java

Channel可分爲可讀和可寫,實現了對應的可讀可寫的Channel接口或者抽象Channel類,就能夠讀寫兼併。數組

 

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

  FileChannel:用於讀取、寫入、映射和操做文件的通道。網絡

  DatagramChannel:經過 UDP 讀寫網絡中的數據通道。dom

  SocketChannel:經過 TCP 讀寫網絡中的數據。socket

  ServerSocketChannel:能夠監聽新進來的 TCP 鏈接,對每個新進來的鏈接都會建立一個 SocketChannel。spa

  以上Channel都實現或者繼承了相應的Channel讀寫接口或者讀寫抽象類,因此都是可讀寫的。可是由於FileChannel能夠根據FileInputStream或者FileOutputStream獲取,因此當根據以上類獲取的FileChennel進行讀或者寫的時候會拋出異常。code

 

獲取Channel對象:

  1. FileChannel對象的獲取:對象

	@Test
	public void test() throws IOException{
		//1. 使用FileInputStream獲取FileChannel
		FileInputStream fis = new FileInputStream("d:\\1.txt");
		FileChannel fChannel = fis.getChannel();
		//2. 使用FileOutputStream獲取FileChannel
		FileInputStream ois = new FileInputStream("d:\\1.txt");
		FileChannel fChannel1 = ois.getChannel();
		//3, 使用RandomAccessFile對象獲取
		RandomAccessFile raf = new RandomAccessFile("d:\\1.txt", "rw");
		FileChannel fChannel2 = raf.getChannel();
		//4. FileChannel的open方法打開
		FileChannel fChannel3 = FileChannel.open(Paths.get("d:\\1.txt"), StandardOpenOption.READ,StandardOpenOption.WRITE);
	}

  

   2. 其餘三個網絡Channel的獲取方式:blog

    @Test
    public void test2() throws IOException{
        SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
        ServerSocketChannel ssChannel = ServerSocketChannel.open();
        DatagramChannel datagramChannel = DatagramChannel.open();
    }

 

Channel的讀寫:

  1. 從Channel中讀取數據到buffer繼承

  public abstract int read(ByteBuffer dst) throws IOException;

  public abstract long read(ByteBuffer[] dsts, int offset, int length)throws IOException;

  public final long read(ByteBuffer[] dsts) throws IOException {
    return read(dsts, 0, dsts.length);
  }

  2. 將buffer中的數據寫入Channel

  public abstract int write(ByteBuffer src) throws IOException;

  public abstract long write(ByteBuffer[] srcs, int offset, int length)throws IOException;

  public final long write(ByteBuffer[] srcs) throws IOException {
    return write(srcs, 0, srcs.length);
  }

  3. 以下一段文件讀寫的代碼

public void test3() {
		FileInputStream fis = null;
		FileChannel inputChannel = null;
		FileOutputStream fos = null;
		FileChannel outputChannel = null;
		try {
			fis = new FileInputStream("d:\\1.txt");
			inputChannel = fis.getChannel();

			fos = new FileOutputStream("d:\\1.bak.txt");
			outputChannel = fos.getChannel();

			ByteBuffer buf = ByteBuffer.allocate(1024);
			int len = -1;
			while ((len = inputChannel.read(buf)) != -1) {
				buf.flip();
				outputChannel.write(buf);
				buf.clear();
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (outputChannel != null) {
				try {
					outputChannel.close();
				} catch (Exception e2) {
				}
			}
			if (outputChannel != null) {
				try {
					outputChannel.close();
				} catch (Exception e2) {
				}
			}
			if (inputChannel != null) {
				try {
					inputChannel.close();
				} catch (Exception e2) {
				}
			}
			if (fos != null) {
				try {
					fos.close();
				} catch (Exception e2) {
				}
			}
			if (fis != null) {
				try {
					fis.close();

				} catch (Exception e2) {
				}
			}

		}

	}

  

Channel的transferFrom和transferTo,看以下代碼(爲了看着簡單異常直接拋出去):

public void test4() throws IOException {
		FileInputStream fis = new FileInputStream("d:\\1.txt");
		FileChannel inputChannel = fis.getChannel();
		FileOutputStream fos = new FileOutputStream("d:\\1.bak.txt");
		FileChannel outputChannel = fos.getChannel();

		// 直接從通道中讀,在內存中分配空間,在物理內存中直接操做
		// inputChannel.transferTo(0,inputChannel.size() , outputChannel);
		outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
	}

 

分散(Scatter)和彙集(Gather)

1. 分散是將一個Channel中的數據寫到多個順序的buffer中,通常是傳進一個buffer數組中,Channel中的數據依次寫入buffer數組中的buffer當中。

                       

 

2. 彙集是將多個buffer中的數據寫入同一個buffer中,通常操做是一個buffer數組。

                       

 

代碼以下:

@Test
public void test5() throws IOException {
	FileInputStream fis = new FileInputStream("d:\\1.txt");
	FileChannel inputChannel = fis.getChannel();
	FileOutputStream fos = new FileOutputStream("d:\\1.bak.txt");
	FileChannel outputChannel = fos.getChannel();

	ByteBuffer buf1 = ByteBuffer.allocate(1024);
	ByteBuffer buf2 = ByteBuffer.allocate(64);
	ByteBuffer buf3 = ByteBuffer.allocate(32);
	ByteBuffer[] bufs = { buf1, buf2, buf3 };

	while (inputChannel.read(bufs) != -1) {
 // 分散讀取(Scattering Reads) inputChannel.read(bufs);

		for (ByteBuffer buf : bufs) {
			buf.flip();
		}
 // 彙集寫入(Gathering Writes) outputChannel.write(bufs);
			
		for (ByteBuffer buf : bufs) {
			buf.clear();
		}
	}
}

  

Channel暫時想到這麼多東西,後續有想到的再補充,開始寫博客歡迎批評指正。

參看資料: 《Java NIO 中文版》

相關文章
相關標籤/搜索