除了基本的讀寫操作, ByteBuf 還提供了它所包含的數(shù)據(jù)的修改方法。
ByteBuf 使用zero-based 的 indexing(從0開始的索引),第一個(gè)字節(jié)的索引是 0,最后一個(gè)字節(jié)的索引是 ByteBuf 的 capacity - 1,下面代碼是遍歷 ByteBuf 的所有字節(jié):
Listing 5.6 Access data
ByteBuf buffer = ...;
for (int i = 0; i < buffer.capacity(); i++) {
byte b = buffer.getByte(i);
System.out.println((char) b);
}
注意通過索引訪問時(shí)不會(huì)推進(jìn) readerIndex (讀索引)和 writerIndex(寫索引),我們可以通過 ByteBuf 的 readerIndex(index) 或 writerIndex(index) 來分別推進(jìn)讀索引或?qū)懰饕?/p>
順序訪問索引
ByteBuf 提供兩個(gè)指針變量支付讀和寫操作,讀操作是使用 readerIndex(),寫操作時(shí)使用 writerIndex()。這和JDK的ByteBuffer不同,ByteBuffer只有一個(gè)方法來設(shè)置索引,所以需要使用 flip() 方法來切換讀和寫模式。
ByteBuf 一定符合:0 <= readerIndex <= writerIndex <= capacity。
Figure 5.3 ByteBuf internal segmentation
1.字節(jié),可以被丟棄,因?yàn)樗鼈円呀?jīng)被讀
2.還沒有被讀的字節(jié)是:“readable bytes(可讀字節(jié))”
3.空間可加入多個(gè)字節(jié)的是:“writeable bytes(寫字節(jié))”
標(biāo)有“可丟棄字節(jié)”的段包含已經(jīng)被讀取的字節(jié)。他們可以被丟棄,通過調(diào)用discardReadBytes() 來回收空間。這個(gè)段的初始大小存儲(chǔ)在readerIndex,為 0,當(dāng)“read”操作被執(zhí)行時(shí)遞增(“get”操作不會(huì)移動(dòng) readerIndex)。
圖5.4示出了在 圖5.3 中的緩沖區(qū)中調(diào)用 discardReadBytes() 所示的結(jié)果。你可以看到,在丟棄字節(jié)段的空間已變得可用寫。需要注意的是不能保證對(duì)可寫的段之后的內(nèi)容在 discardReadBytes() 方法之后已經(jīng)被調(diào)用。
Figure 5.4 ByteBuf after discarding read bytes.
1.字節(jié)尚未被讀出(readerIndex 現(xiàn)在 0)。 2.可用的空間,由于空間被回收而增大。
ByteBuf.discardReadBytes() 可以用來清空 ByteBuf 中已讀取的數(shù)據(jù),從而使 ByteBuf 有多余的空間容納新的數(shù)據(jù),但是discardReadBytes() 可能會(huì)涉及內(nèi)存復(fù)制,因?yàn)樗枰苿?dòng) ByteBuf 中可讀的字節(jié)到開始位置,這樣的操作會(huì)影響性能,一般在需要馬上釋放內(nèi)存的時(shí)候使用收益會(huì)比較大。
ByteBuf 的“可讀字節(jié)”分段存儲(chǔ)的是實(shí)際數(shù)據(jù)。新分配,包裝,或復(fù)制的緩沖區(qū)的 readerIndex 的默認(rèn)值為 0 。任何操作,其名稱以 "read" 或 "skip" 開頭的都將檢索或跳過該數(shù)據(jù)在當(dāng)前 readerIndex ,并且通過讀取的字節(jié)數(shù)來遞增。
如果所謂的讀操作是一個(gè)指定 ByteBuf 參數(shù)作為寫入的對(duì)象,并且沒有一個(gè)目標(biāo)索引參數(shù),目標(biāo)緩沖區(qū)的 writerIndex 也會(huì)增加了。例如:
readBytes(ByteBuf dest);
如果試圖從緩沖器讀取已經(jīng)用盡的可讀的字節(jié),則拋出IndexOutOfBoundsException。清單5.8顯示了如何讀取所有可讀字節(jié)。
Listing 5.7 Read all data
//遍歷緩沖區(qū)的可讀字節(jié)
ByteBuf buffer= ...;
while (buffer.isReadable()) {
System.out.println(buffer.readByte());
}
這段是未定義內(nèi)容的地方,準(zhǔn)備好寫。一個(gè)新分配的緩沖區(qū)的 writerIndex 的默認(rèn)值是 0 。任何操作,其名稱 "write"開頭的操作在當(dāng)前的 writerIndex 寫入數(shù)據(jù)時(shí),遞增字節(jié)寫入的數(shù)量。如果寫操作的目標(biāo)也是 ByteBuf ,且未指定源索引,則源緩沖區(qū)的 readerIndex 將增加相同的量。例如:
writeBytes(ByteBuf dest);
如果試圖寫入超出目標(biāo)的容量,則拋出 IndexOutOfBoundException。
下面的例子展示了填充隨機(jī)整數(shù)到緩沖區(qū)中,直到耗盡空間。該方法writableBytes() 被用在這里確定是否存在足夠的緩沖空間。
Listing 5.8 Write data
//填充隨機(jī)整數(shù)到緩沖區(qū)中
ByteBuf buffer = ...;
while (buffer.writableBytes() >= 4) {
buffer.writeInt(random.nextInt());
}
在 JDK 的 InputStream 定義了 mark(int readlimit) 和 reset()方法。這些是分別用來標(biāo)記流中的當(dāng)前位置和復(fù)位流到該位置。
同樣,您可以設(shè)置和重新定位ByteBuf readerIndex 和 writerIndex 通過調(diào)用 markReaderIndex(), markWriterIndex(), resetReaderIndex() 和 resetWriterIndex()。這些類似于InputStream 的調(diào)用,所不同的是,沒有 readlimit 參數(shù)來指定當(dāng)標(biāo)志變?yōu)闊o效。
您也可以通過調(diào)用 readerIndex(int) 或 writerIndex(int) 將指標(biāo)移動(dòng)到指定的位置。在嘗試任何無效位置上設(shè)置一個(gè)索引將導(dǎo)致 IndexOutOfBoundsException 異常。
調(diào)用 clear() 可以同時(shí)設(shè)置 readerIndex 和 writerIndex 為 0。注意,這不會(huì)清除內(nèi)存中的內(nèi)容。讓我們看看它是如何工作的。 (圖5.5圖重復(fù)5.3 )
Figure 5.5 Before clear() is called
調(diào)用之前,包含3個(gè)段,下面顯示了調(diào)用之后
Figure 5.6 After clear() is called
現(xiàn)在 整個(gè) ByteBuf 空間都是可寫的了。
clear() 比 discardReadBytes() 更低成本,因?yàn)樗皇侵刂昧怂饕?,而沒有內(nèi)存拷貝。
有幾種方法,以確定在所述緩沖器中的指定值的索引。最簡(jiǎn)單的是使用 indexOf() 方法。更復(fù)雜的搜索執(zhí)行以 ByteBufProcessor 為參數(shù)的方法。這個(gè)接口定義了一個(gè)方法,boolean process(byte value),它用來報(bào)告輸入值是否是一個(gè)正在尋求的值。
ByteBufProcessor 定義了很多方便實(shí)現(xiàn)共同目標(biāo)值。例如,假設(shè)您的應(yīng)用程序需要集成所謂的“Flash sockets”,將使用 NULL 結(jié)尾的內(nèi)容。調(diào)用
forEachByte(ByteBufProcessor.FIND_NUL)
通過減少的,因?yàn)樯倭康?“邊界檢查”的處理過程中執(zhí)行了,從而使 消耗 Flash 數(shù)據(jù)變得 編碼工作量更少、效率更高。
下面例子展示了尋找一個(gè)回車符,\ r
的一個(gè)例子。
Listing 5.9 Using ByteBufProcessor to find \r
ByteBuf buffer = ...;
int index = buffer.forEachByte(ByteBufProcessor.FIND_CR);
“衍生的緩沖區(qū)”是代表一個(gè)專門的展示 ByteBuf 內(nèi)容的“視圖”。這種視圖是由 duplicate(), slice(), slice(int, int),readOnly(), 和 order(ByteOrder) 方法創(chuàng)建的。所有這些都返回一個(gè)新的 ByteBuf 實(shí)例包括它自己的 reader, writer 和標(biāo)記索引。然而,內(nèi)部數(shù)據(jù)存儲(chǔ)共享就像在一個(gè) NIO 的 ByteBuffer。這使得衍生的緩沖區(qū)創(chuàng)建、修改其 內(nèi)容,以及修改其“源”實(shí)例更廉價(jià)。
ByteBuf 拷貝
如果需要已有的緩沖區(qū)的全新副本,使用 copy() 或者 copy(int, int)。不同于派生緩沖區(qū),這個(gè)調(diào)用返回的 ByteBuf 有數(shù)據(jù)的獨(dú)立副本。
若需要操作某段數(shù)據(jù),使用 slice(int, int),下面展示了用法:
Listing 5.10 Slice a ByteBuf
Charset utf8 = Charset.forName("UTF-8");
ByteBuf buf = Unpooled.copiedBuffer("Netty in Action rocks!", utf8); //1
ByteBuf sliced = buf.slice(0, 14); //2
System.out.println(sliced.toString(utf8)); //3
buf.setByte(0, (byte) 'J'); //4
assert buf.getByte(0) == sliced.getByte(0);
1.創(chuàng)建一個(gè) ByteBuf 保存特定字節(jié)串。
2.創(chuàng)建從索引 0 開始,并在 14 結(jié)束的 ByteBuf 的新 slice。
3.打印 Netty in Action
4.更新索引 0 的字節(jié)。
5.斷言成功,因?yàn)閿?shù)據(jù)是共享的,并以一個(gè)地方所做的修改將在其他地方可見。
下面看下如何將一個(gè) ByteBuf 段的副本不同于 slice。
Listing 5.11 Copying a ByteBuf
Charset utf8 = Charset.forName("UTF-8");
ByteBuf buf = Unpooled.copiedBuffer("Netty in Action rocks!", utf8); //1
ByteBuf copy = buf.copy(0, 14); //2
System.out.println(copy.toString(utf8)); //3
buf.setByte(0, (byte) 'J'); //4
assert buf.getByte(0) != copy.getByte(0);
1.創(chuàng)建一個(gè) ByteBuf 保存特定字節(jié)串。
2.創(chuàng)建從索引0開始和 14 結(jié)束 的 ByteBuf 的段的拷貝。
3.打印 Netty in Action
4.更新索引 0 的字節(jié)。
5.斷言成功,因?yàn)閿?shù)據(jù)不是共享的,并以一個(gè)地方所做的修改將不影響其他。
代碼幾乎是相同的,但所 衍生的 ByteBuf 效果是不同的。因此,使用一個(gè) slice 可以盡可能避免復(fù)制內(nèi)存。
讀/寫操作主要由2類:
ByteBuf 的各種讀寫方法或其他一些檢查方法可以看 ByteBuf 的 API,下面是常見的 get() 操作:
Table 5.1 get() operations
方法名稱 | 描述 |
---|---|
getBoolean(int) | 返回當(dāng)前索引的 Boolean 值 |
getByte(int) getUnsignedByte(int) | 返回當(dāng)前索引的(無符號(hào))字節(jié) |
getMedium(int) getUnsignedMedium(int) | 返回當(dāng)前索引的 (無符號(hào)) 24-bit 中間值 |
getInt(int) getUnsignedInt(int) | 返回當(dāng)前索引的(無符號(hào)) 整型 |
getLong(int) getUnsignedLong(int) | 返回當(dāng)前索引的 (無符號(hào)) Long 型 |
getShort(int) getUnsignedShort(int) | 返回當(dāng)前索引的 (無符號(hào)) Short 型 |
getBytes(int, ...) | 字節(jié) |
常見 set() 操作如下
Table 5.2 set() operations
方法名稱 | 描述 |
---|---|
setBoolean(int, boolean) | 在指定的索引位置設(shè)置 Boolean 值 |
setByte(int, int) | 在指定的索引位置設(shè)置 byte 值 |
setMedium(int, int) | 在指定的索引位置設(shè)置 24-bit 中間 值 |
setInt(int, int) | 在指定的索引位置設(shè)置 int 值 |
setLong(int, long) | 在指定的索引位置設(shè)置 long 值 |
setShort(int, int) | 在指定的索引位置設(shè)置 short 值 |
下面是用法:
Listing 5.12 get() and set() usage
Charset utf8 = Charset.forName("UTF-8");
ByteBuf buf = Unpooled.copiedBuffer("Netty in Action rocks!", utf8); //1
System.out.println((char)buf.getByte(0)); //2
int readerIndex = buf.readerIndex(); //3
int writerIndex = buf.writerIndex();
buf.setByte(0, (byte)'B'); //4
System.out.println((char)buf.getByte(0)); //5
assert readerIndex == buf.readerIndex(); //6
assert writerIndex == buf.writerIndex();
1.創(chuàng)建一個(gè)新的 ByteBuf 給指定 String 保存字節(jié)
2.打印的第一個(gè)字符,N
3.存儲(chǔ)當(dāng)前 readerIndex 和 writerIndex
4.更新索引 0 的字符B
5.打印出的第一個(gè)字符,現(xiàn)在B
6.這些斷言成功,因?yàn)檫@些操作永遠(yuǎn)不會(huì)改變索引
現(xiàn)在,讓我們來看看 read() 操作,對(duì)當(dāng)前 readerIndex 或 writerIndex 進(jìn)行操作。這些用于從 ByteBuf 讀取就好像它是一個(gè)流。 (對(duì)應(yīng)的 write() 操作用于“追加”到 ByteBuf?。?。下面展示了常見的 read() 方法。
Table 5.3 read() operations
方法名稱 | 描述 |
---|---|
readBoolean() | Reads the Boolean value at the current readerIndex and increases the readerIndex by 1. |
readByte() readUnsignedByte() | Reads the (unsigned) byte value at the current readerIndex and increases the readerIndex by 1. |
readMedium() readUnsignedMedium() | Reads the (unsigned) 24-bit medium value at the current readerIndex and increases the readerIndex by 3. |
readInt() readUnsignedInt() | Reads the (unsigned) int value at the current readerIndex and increases the readerIndex by 4. |
readLong() readUnsignedLong() | Reads the (unsigned) int value at the current readerIndex and increases the readerIndex by 8. |
readShort() readUnsignedShort() | Reads the (unsigned) int value at the current readerIndex and increases the readerIndex by 2. |
readBytes(int,int, ...) | Reads the value on the current readerIndex for the given length into the given object. Also increases the readerIndex by the length. |
每個(gè) read() 方法都對(duì)應(yīng)一個(gè) write()。
Table 5.4 Write operations
方法名稱 | 描述 |
---|---|
writeBoolean(boolean) | Writes the Boolean value on the current writerIndex and increases the writerIndex by 1. |
writeByte(int) | Writes the byte value on the current writerIndex and increases the writerIndex by 1. |
writeMedium(int) | Writes the medium value on the current writerIndex and increases the writerIndex by 3. |
writeInt(int) | Writes the int value on the current writerIndex and increases the writerIndex by 4. |
writeLong(long) | Writes the long value on the current writerIndex and increases the writerIndex by 8. |
writeShort(int) | Writes the short value on the current writerIndex and increases thewriterIndex by 2. |
writeBytes(int,...) | Transfers the bytes on the current writerIndex from given resources. |
Listing 5.13 read()/write() operations on the ByteBuf
Charset utf8 = Charset.forName("UTF-8");
ByteBuf buf = Unpooled.copiedBuffer("Netty in Action rocks!", utf8); //1
System.out.println((char)buf.readByte()); //2
int readerIndex = buf.readerIndex(); //3
int writerIndex = buf.writerIndex(); //4
buf.writeByte((byte)'?'); //5
assert readerIndex == buf.readerIndex();
assert writerIndex != buf.writerIndex();
1.創(chuàng)建一個(gè)新的 ByteBuf 保存給定 String 的字節(jié)。
2.打印的第一個(gè)字符,N
3.存儲(chǔ)當(dāng)前的 readerIndex
4.保存當(dāng)前的 writerIndex
5.更新索引0的字符 B
6.此斷言成功,因?yàn)?writeByte() 在 5 移動(dòng)了 writerIndex
Table 5.5 Other useful operations
方法名稱 | 描述 |
---|---|
isReadable() | Returns true if at least one byte can be read. |
isWritable() | Returns true if at least one byte can be written. |
readableBytes() | Returns the number of bytes that can be read. |
writablesBytes() | Returns the number of bytes that can be written. |
capacity() | Returns the number of bytes that the ByteBuf can hold. After this it will try to expand again until maxCapacity() is reached. |
maxCapacity() | Returns the maximum number of bytes the ByteBuf can hold. |
hasArray() | Returns true if the ByteBuf is backed by a byte array. |
array() | Returns the byte array if the ByteBuf is backed by a byte array, otherwise throws an |
UnsupportedOperationException.
更多建議: