ByteBuffer作為JDK的字節流處理對象,這里舉個小例子說明下用法,直接上代碼:
package com.wlf.netty.nettyserver; import org.junit.Assert; import org.junit.Test; import java.nio.ByteBuffer; public class ByteBufferTest { @Test public void byteBufferTest() { // 寫入消息體 ByteBuffer byteBuffer = ByteBuffer.allocate(10); byteBuffer.putInt(0xabef0101); byteBuffer.putInt(1024); // 今天過節 byteBuffer.put((byte) 1); byteBuffer.put((byte) 0); // 讀取消息頭,因為寫完后position已經到10了,所以需要先反轉為0,再從頭讀取 byteBuffer.flip(); printDelimiter(byteBuffer); // 讀取length printLength(byteBuffer); // 繼續讀取剩下數據 byteBuffer.get(); byteBuffer.get(); printByteBuffer(byteBuffer); // 我再反轉一下,我還可以從頭開始讀 byteBuffer.flip(); printDelimiter(byteBuffer); // clear清空一下,再從頭開始讀 byteBuffer.clear(); printDelimiter(byteBuffer); // rewind重繞一下 byteBuffer.rewind(); printDelimiter(byteBuffer); // mark標記一下 byteBuffer.mark(); // 再讀取length printLength(byteBuffer); // reset重置,回到讀取delimiter的地方 byteBuffer.reset(); printByteBuffer(byteBuffer); } private void printDelimiter(ByteBuffer buf) { int newDelimiter = buf.getInt(); System.out.printf("delimeter: %s\n", Integer.toHexString(newDelimiter)); printByteBuffer(buf); } private void printLength(ByteBuffer buf) { int length = buf.getInt(); System.out.printf("length: %d\n", length); printByteBuffer(buf); } private void printByteBuffer(ByteBuffer buf) { System.out.printf("position: %d, limit: %d, capacity: %d\n", buf.position(), buf.limit(), buf.capacity()); } }
輸出結果:
delimeter: abef0101 position: 4, limit: 10, capacity: 10 length: 1024 position: 8, limit: 10, capacity: 10 position: 10, limit: 10, capacity: 10 delimeter: abef0101 position: 4, limit: 10, capacity: 10 delimeter: abef0101 position: 4, limit: 10, capacity: 10 delimeter: abef0101 position: 4, limit: 10, capacity: 10 length: 1024 position: 8, limit: 10, capacity: 10 position: 4, limit: 10, capacity: 10 Process finished with exit code 0
ByteBuffer的索引是唯一的。像上面的例子,初始索引是0,寫完索引值為9,為了讀取寫入的值,我們再重新設置索引為0(調用flip方法)。ByteBuffer有4個索引值,分別是:
mask:就是你標記的索引,標記唯一的作用是調用reset重置回到過去
position:當前位置的索引,mask標記任何時候都不會大於position,因為你必須先讀到當前位置之后,才能標記該位置;同時position也不能超過limit限制
limit:第一個不應該讀取或寫入的元素的索引,也就是讀寫禁地,默認是最大容量,如果你設置該值,那么理所讓然它不能超過最大容量capacity
capacity:這個就不解釋了
它們的大小關系始終是:
mask <= position <= limit <= capacity
我們上面的例子中就是capacity=limit。
初始索引:
+-----------------------------------------------------+
/ bytes /
+-----------------------------------------------------+
/ 10 /
0=position 10=limit=capacity
我們寫入delimiter之后:
+----------------+------------------------------------+
/ delimiter / other bytes /
+----------------+------------------------------------+
/ 4 / 6 /
0 4=position 10=limit=capacity
至於反轉flip如何切換讀寫模式、reset如何重置標記、clear清除如何重新設置索引值為0、rewind重繞如何讓你重新讀取,都不會動內容,所以你會看到上面不管怎么折騰我們都可以重復取出delimiter、length的值。看下源碼就一清二楚了,無非就是對上面4個索引值進行賦值而已:
/** * Resets this buffer's position to the previously-marked position. * * <p> Invoking this method neither changes nor discards the mark's * value. </p> * * @return This buffer * * @throws InvalidMarkException * If the mark has not been set */ public final Buffer reset() { int m = mark; if (m < 0) throw new InvalidMarkException(); position = m; return this; } /** * Clears this buffer. The position is set to zero, the limit is set to * the capacity, and the mark is discarded. * * <p> Invoke this method before using a sequence of channel-read or * <i>put</i> operations to fill this buffer. For example: * * <blockquote><pre> * buf.clear(); // Prepare buffer for reading * in.read(buf); // Read data</pre></blockquote> * * <p> This method does not actually erase the data in the buffer, but it * is named as if it did because it will most often be used in situations * in which that might as well be the case. </p> * * @return This buffer */ public final Buffer clear() { position = 0; limit = capacity; mark = -1; return this; } /** * Flips this buffer. The limit is set to the current position and then * the position is set to zero. If the mark is defined then it is * discarded. * * <p> After a sequence of channel-read or <i>put</i> operations, invoke * this method to prepare for a sequence of channel-write or relative * <i>get</i> operations. For example: * * <blockquote><pre> * buf.put(magic); // Prepend header * in.read(buf); // Read data into rest of buffer * buf.flip(); // Flip buffer * out.write(buf); // Write header + data to channel</pre></blockquote> * * <p> This method is often used in conjunction with the {@link * java.nio.ByteBuffer#compact compact} method when transferring data from * one place to another. </p> * * @return This buffer */ public final Buffer flip() { limit = position; position = 0; mark = -1; return this; } /** * Rewinds this buffer. The position is set to zero and the mark is * discarded. * * <p> Invoke this method before a sequence of channel-write or <i>get</i> * operations, assuming that the limit has already been set * appropriately. For example: * * <blockquote><pre> * out.write(buf); // Write remaining data * buf.rewind(); // Rewind buffer * buf.get(array); // Copy data into array</pre></blockquote> * * @return This buffer */ public final Buffer rewind() { position = 0; mark = -1; return this; }
