1. 程式人生 > >略解ByteBuf

略解ByteBuf

white 我們 時也 courier ons small 基於 他會 input

說到ByteBuf,我們並不陌生,官網給的解釋為,一個可以進行隨機訪問或者是順序訪問的字節集合,它是NIO buffers緩沖的底層抽象。既然是底層抽象,那麽我們就可以基於其衍生出很多的具體實現出來,事實上,netty中的很多緩沖組件都是基於此抽象類做的擴展。

隨機訪問索引

和普通的字節數據一樣,ByteBuf也是從0開始索引的。這就意味著第一個字節的索引永遠是0,而最後一個字節的索引則是capacity。舉個例子,當我們去遍歷緩沖中的所有字節的時候,我們可以按照如下方式來做:

 ByteBuf buffer = ...;
 for (int i = 0; i < buffer.capacity(); i ++) {
     byte
b = buffer.getByte(i); System.out.println((char) b); }

可以清楚的看到,其使用方式和遍歷字節數組一樣的做法。我們可以隨機的獲取緩沖區裏面的任意一個字節。

順序訪問索引

這個ByteBuf的實現中,有三個比較有意思的屬性,readerIndex,writerIndex,capacity,從字面意思上,我們可以理解為讀索引,寫索引,容量。下圖則展示了三個屬性之間的關系:

技術分享圖片

首先是readable bytes(可讀字節數組),裏面放置的是真實的數據,當使用帶有read或者是skip的方法來操作此數據內容的時候,都將導致readerIndex遞增。如果當前內容讀取完畢,沒有更多的內容可以讀取,那麽嘗試讀取將會拋出IndexOutOfBoundsException。默認情況下,一個新分配的緩沖區或者包裝的緩沖區或者復制的緩沖區,其readerIndex的初始值為0。示例讀取代碼如下:

// Iterates the readable bytes of a buffer.
 ByteBuf buffer = ...;
 while (buffer.isReadable()) {
     System.out.println(buffer.readByte());
 }

其次是writable bytes(可寫字節數組),裏面是空數據,待被真實數據覆蓋。當使用帶有write的方法來操作此數據內容的時候,都將導致writerIndex遞增。如果當前已無足夠的空間可寫,那麽嘗試寫入將會拋出IndexOutOfBoundsException。默認情況下,一個新分配的緩存區,其writerIndex的初始值為0。包裝的緩沖區或者復制的緩沖區,其writerIndex等於capacity。示例寫入代碼如下:

 // Fills the writable bytes of a buffer with random integers.
 ByteBuf buffer = ...;
 while (buffer.maxWritableBytes() >= 4) {
     buffer.writeInt(random.nextInt());
 }
 

最後是discardable bytes(廢棄字節數組),此數組裏面是已經讀取過的數據。開始的時候,其值默認為0,但是當進行讀取操作的時候,它的值開始慢慢遞增,直至和writerIndex相等。這些字節可以通過調用discardReadBytes()方法來進行釋放,釋放前和釋放後的圖示示例如下:

釋放前:

技術分享圖片

釋放後:

技術分享圖片

需要註意的是,不同緩沖區的底層實現,可能會讓writable bytes裏面填充進完全不同的數據,所以使用此方法的時候,還請審慎。

你可以調用clear()方法來重置readerIndex和writerIndex為0。此方法不會清理掉真實的數據,而是僅僅重置這兩個索引。

清理前:

技術分享圖片

清理後:

技術分享圖片

需要註意的是,此種情況下可能會覆蓋原有緩沖數據,使用的事情請謹慎。

搜索操作

簡單的單個字節搜索,可以使用indexOf(int, int, byte)bytesBefore(int, int, byte)來實現。bytesBefore(byte) 適用於簡單的String搜索。 forEachByte(int, int, ByteBufProcessor) 適用於比較復雜的搜索。

創建緩沖副本

你可以使用duplicate(), slice() 或者 slice(int, int)來創建已有緩沖的衍生副本。衍生的緩沖副本將會有獨立的readerIndex,writerIndex和標記索引,但是如同其他NIO 緩沖區一樣,他會共享內部的數據。為了能夠獲得一份真正的全新的緩沖拷貝,可以使用copy()方法來進行。需要註意的是,衍生的緩沖將不會調用retain()方法,因為其reference count將不會增加。

JDK類型轉換

你可以使用array()方法來使ByteBuf支持字節數組(比如 byte[])。同時也可以使用hasArray()方法來檢測其是否支持字節數組。

你可以使用nioBuffer()方法來使ByteBuf支持NIO ByteBuffer。同時也可以使用nioBufferCount()方法來檢測其是否能夠被轉換為NIO buffer。

你可以使用toString(Charset)方法來使ByteBuf轉換成String。需要註意的是,toString()方法並非類型轉換的方法。

你可以使用ByteBufInputStream和ByteBufOutputStream來進行IO流的轉換。

略解ByteBuf