Netty字節(jié)級(jí)別的操作

2020-11-11 09:03 更新

除了基本的讀寫操作, ByteBuf 還提供了它所包含的數(shù)據(jù)的修改方法。

隨機(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

Figure%205

1.字節(jié),可以被丟棄,因?yàn)樗鼈円呀?jīng)被讀

2.還沒有被讀的字節(jié)是:“readable bytes(可讀字節(jié))”

3.空間可加入多個(gè)字節(jié)的是:“writeable bytes(寫字節(jié))”

可丟棄字節(jié)的字節(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.

Figure%205

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ì)比較大。

可讀字節(jié)

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

Figure%205

調(diào)用之前,包含3個(gè)段,下面顯示了調(diào)用之后

Figure 5.6 After clear() is called

Figure%205

現(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ū)

“衍生的緩沖區(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類:

  • get()/set() 操作從給定的索引開始,保持不變
  • read()/write() 操作從給定的索引開始,與字節(jié)訪問的數(shù)量來適用,遞增當(dāng)前的寫索引或讀索引

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.


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)