理解NIO中的Buffer與Channel

本節主要講Socket與NIO

   首先Socket,他不是協議,而是計算機之間的通信技術,HTTP纔是應用層協議。Socket與HTTP的關係是Socket是HTTP的底層實現,HTTP對Socket進行封裝,所以我們學習Socket技術還是很有必要的

  Socket底層是用C++實現的,當然最底層都是直接與硬件進行通信,這裏不做探究。各個語言如:Java,C#都對Socket都有不同程度的封裝。

  NIO即non-blocking IO,無阻塞通信

NIO與BIO最大的不同就在於NIO是以塊爲單位進行傳輸,BIO是以字節流爲單位進行傳輸;

什麼是以塊爲單位進行傳輸?

   NIO兩個最重要的概念是Buffer和Channel,一個很有意思的比喻是Channel是一個通道,而Buffer是小礦車,當小礦車裝滿或者命令小礦車移動的時候小礦車拉着慢慢的數據在通道上行走。

  Buffer(緩衝區)

  一個抽象類,是所有Buffer類的爸爸,像什麼IntBuffer,FloatBuffer等都繼承自它,不過IntBuffer也是一個抽象類,所有xxxBuffer類都是抽象類,無法直接實例化,他們之間實例化都是用wrap方法:

        int[] intArray = {0,1,2,3,4,5,6,7,8,9};
        IntBuffer wrap = IntBuffer.wrap(intArray);
     
        System.out.println(wrap);

除此之外還有一個帶有兩個參數的構造方法:

offset:要使用的子數組的偏移量;必須爲非負且不大於 array.length。將新緩衝區的位置設置爲此值。

length: 要使用的子數組的長度;必須爲非負且不大於 array.length - offset。將新緩衝區的界限設置爲 offset + length。

    public static IntBuffer wrap(int[] array,
                                    int offset, int length)
    {
        try {
            return new HeapIntBuffer(array, offset, length);
        } catch (IllegalArgumentException x) {
            throw new IndexOutOfBoundsException();
        }
    }

如果我們觀察Buffer,會發現他有四個重要的參數:

   // Invariants: mark <= position <= limit <= capacity
    private int mark = -1;
    private int position = 0;
    private int limit;
    private int capacity;

mark:設置標記,mark大於positon時重置爲-1;像是爬山時候的路標
position:緩衝區的當前索引
limit:緩衝區的限制
capacity:緩衝區的容量,且limit<=capacity

跟着四個數據最直接相關的就是filp()方法了

    public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }

比如緩衝區有10byte,只用了8byte,那麼此時position在8,調用該方法之後limit變爲8position變爲0

還有clear()方法:清除此緩衝區。將位置設置爲 0,將限制設置爲容量,並丟棄標記。

申請直接緩衝區

allocateDirect:創建直接緩衝區,其操作數據不在jvm在內核中;善於保存那些一手操作系統本機IO操作影響的大量,長時間保存的數據;操作快但不安全
allocate:非直接緩衝區,其調用的是HeapBuffer方法,從名字就能看出:嗯,申請在堆上了;事實上默認的wrap就是調用heapBuffer方法,其繼承關係十分奇怪,我是看不出來個啥。。。。(並沒有heapBuffer方法,而是對應的如HeapIntBuffer,HeapFloatBuffer等方法)

總的來說,Buffer的子類只有ByteBuffer和CharBuffer比較常用。

Channel通道

  Channel類中全是接口,這是由於通道的功能實現是要依賴於操作系統的,Channel接口只定義有哪些功能,而具體的功能實現在不同操作系統中不一樣,因此,JDK通道被設計成接口類型

比如果我們常用的FileChannel

public abstract class FileChannel
    extends AbstractInterruptibleChannel
    implements SeekableByteChannel, GatheringByteChannel, ScatteringByteChannel

其基本使用·方式是這樣:

 

FileOutputStream file =
                new FileOutputStream(new File("D:\\a.txt"));
        FileChannel channel = file.getChannel();
        ByteBuffer buffer = ByteBuffer.wrap("abcde".getBytes());
        try {
            //返回值就是position的位置
            int write = channel.write(buffer);
            //channel內部維護位置,可以重寫定義position
            channel.position(2);
            //還原緩衝區buffer中的position
            buffer.rewind();
            //再次寫入
            channel.write(buffer);

        } catch (IOException e) {
            e.printStackTrace();
        }

D盤中的a.txt文件是這樣子的:

其主要API如下:

write()方法是具有同步性的,即多個線程之間某一通道要進行傳輸的話會獨佔線程,其他線程進入阻塞狀態

read()方法也是同步的,其返回值是:從通道當前位置向ByteBuffer緩衝區中讀的字節個數;當返回-1即到達流的末端

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章