netty極簡教程(二): nio Buffer的原理及使用

我們知道,netty直接與jdk的原生nio開發的,可以說是jdk nio的增強,所以理解jdk nio的機制就變得非常重要,接下來將介紹jdk中關於nio的幾個非常重要的組件, 示例源碼: https://github.com/jsbintask22/netty-learning

ByteBuffer介紹

對比jdk bio的寫法,在從Socket讀取數據的時候,不管是客戶端還是服務端,我們通常需要將字節讀取到一個字節數組,知道讀取的數據長度滿足所需要的條件:


InputStream is = socket.getInputStream();
byte[] buff = new byte[1024];
int len;

while ((len = is.read(buff)) != -1) {
    System.out.println("client msg: " + new String(buff, 0, len));
}

這裏的buff我們就可以認爲是一個數據容器,後續的其他處理(比如這裏的 new String)都會從這個數據容器中取。


而在nio編程中,這個字節數組改爲了一個可重複使用的容器Buffer(java.nio.Buffer),它有多種同類型的基本類型子類,它的結構如下:


假設有一個長度爲n的容器。

  1. position指針代表洗一個可以讀或者可以寫入的位置索引,初始狀態則指向0
  2. limit代表下一個不能寫入或者讀取的位置,初始位置則指向n(注意索引是從0開始的)
  3. capacity總是表示容器的大小,總是爲n
  4. mark作爲一個標誌爲,當調用mark()方法時,會將position賦值給mark, 這樣在下次調用reset()方法的時候可以讓position回到上次標誌的位置
    有了以上這些,在寫入或者讀取時總是會移動position的位置,而如果想重置復原則需要移動. 它們直接總是會滿足這樣的關係: mark <= position <= limit <= capacity

這裏指的注意的是 position 代表的是下一個可以讀或者可以寫的位置,所以讀寫均會移動位置。

Buffer使用

以IntBuffer爲例,介紹下它的使用,IntBuffer繼承自Buffer,內部使用一個整型數組存儲數據 final int[] hb;


  1. 向容器中存儲數據: put(int i);, put(int index, int i); 從指定位置的存儲不會移動 position 指針的位置。
  2. 獲取數據: int get();, int get(int index); 從指定索引位置獲取數據不會移動 positon 位置.
  3. 檢查是否有有效數據: 檢查position 是否小於limit即可 .return position < limit;
IntBuffer intBuffer = IntBuffer.allocate(10); // 1
for (int i = 0; i < intBuffer.capacity(); i++) {
    intBuffer.put((i + 1));  // 2
}

intBuffer.flip();  // 3
while (intBuffer.hasRemaining()) {  // 4
    int i = intBuffer.get();  // 5
    System.out.println(i);
}

intBuffer.rewind();
while (intBuffer.hasRemaining()) {
    int i = intBuffer.get();
    System.out.println(i);
}
  1. 分配一個字節數據,默認在heap上(內部一個長度爲10的整型數據)
  2. 寫入數據,會移動 position 的位置
  3. 翻轉數據區域,將已經寫入了數據的區域用limit和position標識出來,即: limit = position; position = 0; remark = -1;
  4. 檢查是否有有效數據.
  5. 獲取當前position指針的數據,並且向前移動

最後介紹下 rewind, flip, clear三個方法的區別

  1. clear爲將所有指針重置爲初始狀態:position = 0; limit = capacity; mark = -1;,一般用於在進行一系列讀寫操作後需要重用該容器進行重置.
  2. flip爲將有效數據隔離,比如put數據後,調用該方法將數據讀取出來 limit = position; position = 0; remark = -1;
  3. rewind用於讀取了數據後需要重複讀取數據,即將position指針重置爲0即可, position = 0; mark = -1;

總結

  1. 在bio編程中,我們通常使用字節數據向socket寫入數據或者讀取數據到字節數據,而在nio中則一般使用Buffer進行該操作,它有多個基本類型子類,最常用的即是ByteBuffer
  2. Buffer的讀寫原理爲移動其中的position,limit指針
  3. rewind,flip, clear三者區別

擴展: 對於Buffer而言,直接使用子類的allocate方法分配數組在heap上,它還可以分配在堆外內存,稱爲直接內存,可以實現零拷貝,提高效率

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