【稀裏糊塗學Netty】Nio Buffer

本篇文章介紹一下java中的Nio中的Buffer,注意不是Netty中的,後面會講解Netty中的Buffer

什麼是Buffer

Buffer本質就是一個可以讀寫的內存塊,它提供了一些方法,可以輕鬆的往裏面寫數據和讀數據。
Nio中Channel提供了從文件或者網絡讀取數據的渠道,但是讀取和寫入數據時,必須經由Buffer

Nio中Buffer有以下幾種:

  • IntBuffer
  • FloatBuffer
  • CharBuffer
  • DoubleBuffer
  • ShortBuffer
  • LongBuffer
  • ByteBuffer

可以看出八種基本數據類型除了boolean,都有一個對應的Buffer

下面以IntBuffer爲例講解一下Buffer中的方法,然後通過實例展示

  • public static IntBuffer allocate(int capacity)
    創建一個IntBuffer對象。指定這個IntBuffer的容量
    //可以放5個數字到這個buffer中
    IntBuffer intBuffer = IntBuffer.allocate(5);
  • public abstract IntBuffer put(int i);
    向IntBuffer中放一個整數
    //創建一個存放5個整數的buffer
    IntBuffer intBuffer = IntBuffer.allocate(5);
    //將整數1放到buffer中
    intBuffer.put(1);
  • public final Buffer flip()
    切換buffer的讀寫。內部源碼實際的邏輯就是通過控制position的值來進行操作的
    
  • public final boolean hasRemaining()
    判斷buffer中是否有數據,一般在while循環中當作條件。

例子:

public class BufferTest {
  public static void main(String[] args) throws Exception {

    IntBuffer intBuffer = IntBuffer.allocate(5);
    //intBuffer.put(1);

    for (int i = 0; i < intBuffer.capacity(); i++) {
      intBuffer.put(i);
    }

    // 如何從buffer讀數據,通過這個方法將buffer切換爲模式,然後就可以讀數據,
    // 如果想往buffer裏再寫數據,就在執行一次這個方法轉換一下就行
    intBuffer.flip();

    // 如果intBuffer中還有數據
    while (intBuffer.hasRemaining()) {
      System.out.println(intBuffer.get());
    }
  }
}

執行結果爲:


下面通過源碼,來看一下

  1. buffer中的數據從在那?

    在上面的實例中,我們創建了一個int的buffer,可以保存4個數據,那麼這4個數據其實就是保存在這個紅框的hb數組中的。(8個buffer都會有一個對應的hb)。
  2. buffer中的4個屬性的意思

    屬性 描述
    capacity buffer可以存放的最大數據,創建buffer是確定,然後就不可以在變了
    limit 表示緩衝區的當前終點。就是設置以後,讀寫buffer時不能超過這個limit的位置
    position 表示將要讀取數據的位置或者寫入數據的位置
    mark 標記


    position爲0表示要將數據寫入到的位置是下標爲0的位置,下一次就會變爲1以此類推。但是因爲limit爲5,則表示當position爲4時就不能再添加了。這就是limit的作用。如果現在吧limit設置爲1,那麼添加完第一數據後,就不能再添加了,即便是我們開始設置的容量capacity爲5,也不能再添加數據了,這就是limit的作用。

常用的api解釋

 

Nio還支持通過多個Buffer(即Buffer數組)完成讀寫操作,也就是Scattering 和Gathering
什麼是Scattering?
將數據寫入buffer時,可以依次寫入,比如一個buffer寫滿了,就往buffer數組的下一個buffer裏寫
什麼是Gathering
從buffer讀數據時,可以採用buffer數組,比如一個buffer讀完之後,就從buffer數組的下一個buffer中繼續讀。

下面通過例子看一下:
 

public static void main(String[] args) throws Exception {

    ServerSocketChannel serverSocketChannel =ServerSocketChannel.open();
    InetSocketAddress inetSocketAddress = new InetSocketAddress(7000);
    serverSocketChannel.socket().bind(inetSocketAddress);

    ByteBuffer[] byteBuffers = new ByteBuffer[2];
    byteBuffers[0] = ByteBuffer.allocate(5);
    byteBuffers[1] = ByteBuffer.allocate(3);

    SocketChannel socketChannel = serverSocketChannel.accept();
    int messageLength = 8; //假定從客戶端接收8個字節
    while (true) {
      int byteRead = 0;
      while (byteRead < messageLength) {
        long l = socketChannel.read(byteBuffers);// 讀取的字節數
        byteRead += l; // 累計讀取的字節數
        System.out.println("byteRead=" + byteRead);
        // 使用流打印,看看當前buffer的position和limit
        Arrays.asList(byteBuffers).stream().map(buffer->"position:" + buffer.position() + "limit:" + buffer.limit()).forEach(System.out::println);
      }
      //對所有的buffer進行flip
      Arrays.asList(byteBuffers).forEach(buffer->buffer.flip());
      //將數據讀出,顯示到客戶端
      long byteWrite = 0;
      while(byteWrite < messageLength) {
        long l = socketChannel.write(byteBuffers);
        byteWrite += l;
      }

      //將所有的buffer進行clean
      Arrays.asList(byteBuffers).forEach(buffer->buffer.clear());
      System.out.println("byteRead=" + byteRead + "byteWrites=" + byteWrite + "messageLength=" + messageLength);

    }
}

運行啓動程序後,然後通過cmd啓動telenet,方式如下:
1.win + R,然後輸入cmd
2.打開cmd後,輸入telnet 127.0.0.1 7000 連接我們的socket
3.連接成功後,輸入helloa六個字符

結果:然後可以發現helloa在填充完第一個數組後,繼續想第二個數組中填寫。然後從字節數組中讀的時候,也是把兩個數組都讀出來了。

 

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