Netty ByteBuf 傳輸載體
1. 前言
在 Netty 里面的數(shù)據(jù)讀寫是以 ByteBuf 為單位進(jìn)行交互的,ByteBuf 是一個(gè)字節(jié)容器,如果了解過 NIO 的同學(xué)應(yīng)該知道,在 NIO 里面也有類型的數(shù)據(jù)載體 ByteBuffer。
2. 學(xué)習(xí)目的
熟悉掌握 ByteBuf 的原理及 API,則可以自定義通信協(xié)議,并且使用 ByteBuf、序列化等技術(shù)實(shí)現(xiàn)通信協(xié)議,并且有效解決拆包和粘包問題(后面章節(jié)會(huì)詳細(xì)分析)。
3. ByteBuf 結(jié)構(gòu)
ByteBuff 的結(jié)構(gòu)主要由四個(gè)部分組成,廢棄字節(jié)、可讀字節(jié)、可寫字節(jié)、可擴(kuò)容字節(jié),具體結(jié)構(gòu)圖如下所示:
通過以上的結(jié)構(gòu)圖,我們可以看出它其實(shí)就是一個(gè)數(shù)組,在寫數(shù)據(jù)和讀數(shù)據(jù)的時(shí)候分別維護(hù)兩個(gè)指針的移動(dòng),分別是 readerIndex 和 writerIndex,在 readerIndex 和 writerIndex 之間的數(shù)據(jù)是有效可讀數(shù)據(jù)。具體分析如下所示:
- ByteBuf 的四個(gè)核心屬性,分別是 readerIndex(可讀指針位置)、writerIndex(可寫指針位置)、capacity(初始化容量值)、maxCapacity(最大容量值)。其中 readerIndex 和 writerIndex 之間的數(shù)據(jù)是 ByteBuf 的主體數(shù)據(jù);
- 讀取數(shù)據(jù)的時(shí)候,readerIndex 遞增,一旦 readerIndex 等于 writerIndex 則表示該容器沒有數(shù)據(jù)可讀了。writerIndex-readerIndex 表示有效可讀數(shù)據(jù)的長度;
- 寫數(shù)據(jù)的時(shí)候,writerIndex 遞增,一旦 writerIndex 等于 capacity 表示容器已經(jīng)滿了,ByteBuf 不能再寫數(shù)據(jù)了,capacity-writerIndex 表示容器還可以寫入的數(shù)據(jù)長度;
- 當(dāng)向 ByteBuf 寫數(shù)據(jù)的時(shí)候,如果容量不足,那么這個(gè)時(shí)候可以進(jìn)行擴(kuò)容,直到 capacity 擴(kuò)容到 maxCapacity,超過 maxCapacity 就會(huì)報(bào)錯(cuò);
- 總結(jié),readerIndex<=writerIndex<=capacity<=maxCapacity。
4. 核心 API
方法 | 描述 |
---|---|
capacity() | 容量 |
maxCapacity() | 最大容量(當(dāng)容量最大時(shí),還可以擴(kuò)容) |
readableBytes() | 可讀字節(jié)數(shù) |
isReadable() | 是否可讀 |
writableBytes() | 可寫字節(jié)數(shù) |
isWritable() | 是否可寫 |
maxWritableBytes() | 最大可寫字節(jié)數(shù) |
readerIndex() | 讀指針 |
readerIndex(int) | 重置讀指針為某個(gè)位置 |
writerIndex() | 寫指針 |
writeIndex(int) | 重置寫指針為某個(gè)位置 |
markReaderIndex() | 保存當(dāng)前讀指針 |
resetReaderIndex() | 回歸之前保存的讀指針 |
markWriterIndex() | 保存當(dāng)前寫指針 |
resetWriterIndex | 回歸之前保存的寫指針 |
writeByte(int i) | 寫一個(gè)字節(jié) |
writeBytes(byte[] bytes) | 寫一個(gè)字節(jié)數(shù)組 |
readByte() | 讀一個(gè)字節(jié) |
readByte(byte[] bytes) | 讀一個(gè)字節(jié)數(shù)組(并往參數(shù) bytes 里存放) |
以上是 ByteBuf 的核心的 API,很多時(shí)候,在編程的時(shí)候直接操作 ByteBuf 可能會(huì)相對(duì)的繁瑣,所以不會(huì)直接手工調(diào)用這些 API,而是通過封裝編碼和解碼器的方式進(jìn)行使用,但是編碼、解碼底層就是通過 ByteBuf 去實(shí)現(xiàn)的。
5. 核心 API 詳解
5.1 capatiy()
表示 ByteBuf 可以寫入多少個(gè)字節(jié),一般在初始化 ByteBuf 時(shí)就會(huì)指定。
實(shí)例:
ByteBuf byteBuf=Unpooled.buffer(10);
5.2 maxCapacity()
表示 ByteBuf 最大可以支持多少字節(jié),如果當(dāng) writerIndex=capacity 時(shí),會(huì)判斷 capacity 是否等于 maxCapacity,如果小于則擴(kuò)容。
實(shí)例:
//參數(shù)1,容量值
//參數(shù)2,最大容量值
ByteBuf byteBuf = Unpooled.buffer(10,20);
5.3 readalbeBytes()
表示 ByteBuf 當(dāng)前可讀的字節(jié)數(shù),它的值等于 writerIndex-readerIndex。
源碼:
public int readableBytes() {
return this.writerIndex - this.readerIndex;
}
5.4 isReadable()
如果 writerIndex 和 readerIndex 相等,則不可讀,isReadable () 方法返回 false。
源碼:
public boolean isReadable(int numBytes) {
return this.writerIndex - this.readerIndex >= numBytes;
}
5.5 writableBytes()
表示 ByteBuf 當(dāng)前可寫的字節(jié)數(shù),它的值等于 capacity-writerIndex。
源碼:
public int writableBytes() {
return this.capacity() - this.writerIndex;
}
5.6 isWritable()
如果 capacity 和 writerIndex 相等,則表示不可寫,isWritable () 返回 false。
源碼:
public boolean isWritable() {
return this.capacity() > this.writerIndex;
}
5.7 maxWritableBytes()
capacity 和 writerIndex 相等,并不代表不能繼續(xù)往 ByteBuf 寫數(shù)據(jù)了。如果發(fā)現(xiàn)往 ByteBuf 中寫數(shù)據(jù)寫不進(jìn)去的話,Netty 會(huì)自動(dòng)擴(kuò)容 ByteBuf,直到擴(kuò)容到底層的內(nèi)存大小為 maxCapacity,而 maxWritableBytes () 就表示可寫的最大字節(jié)數(shù),它的值等于 maxCapacity-writerIndex。
源碼:
public int maxWritableBytes() {
return this.maxCapacity() - this.writerIndex;
}
5.8 readerIndex()
readerIndex () 表示返回當(dāng)前的讀指針 readerIndex,ByteBuf 會(huì)維護(hù)一個(gè)變量 readerIndex。
源碼:
int readerIndex;
public int readerIndex() {
return this.readerIndex;
}
5.9 readerIndex(int)
readerIndex (int) 表示設(shè)置讀指針,比如說可以回滾到某個(gè)指針位置。
源碼:
public ByteBuf readerIndex(int readerIndex) {
if (readerIndex >= 0 && readerIndex <= this.writerIndex) {
//給readerIndex賦值
this.readerIndex = readerIndex;
return this;
} else {
throw new IndexOutOfBoundsException("....");
}
}
5.10 writeIndex()
writeIndex () 表示返回當(dāng)前的寫指針 writerIndex。
源碼:
public int writerIndex() {
return this.writerIndex;
}
5.11 writeIndex(int)
writeIndex (int) 表示設(shè)置寫指針,比如說可以回滾到某個(gè)指針位置。
public ByteBuf writerIndex(int writerIndex) {
if (writerIndex >= this.readerIndex && writerIndex <= this.capacity()) {
//給writerIndex賦值
this.writerIndex = writerIndex;
return this;
} else {
throw new IndexOutOfBoundsException("...");
}
}
5.12 markReaderIndex()
markReaderIndex () 表示把當(dāng)前的讀指針保存起來,其實(shí)類似數(shù)據(jù)庫事務(wù)的當(dāng)前狀態(tài)標(biāo)記。
源碼:
public ByteBuf markReaderIndex() {
//使用markedReaderIndex保存當(dāng)前讀指針
this.markedReaderIndex = this.readerIndex;
return this;
}
5.13 resetReaderIndex()
resetReaderIndex () 表示把當(dāng)前的讀指針恢復(fù)到之前保存的值,類似數(shù)據(jù)庫事務(wù)回歸到某個(gè)狀態(tài)。
源碼:
public ByteBuf resetReaderIndex() {
this.readerIndex(this.markedReaderIndex);
return this;
}
5.14 markWriterIndex()
表示把當(dāng)前的寫指針保存起來。
源碼:
public ByteBuf markWriterIndex() {
this.markedWriterIndex = this.writerIndex;
return this;
}
5.15 resetWriterIndex()
切塊之前保存的寫指針 writerIndex。
源碼:
public ByteBuf resetWriterIndex() {
this.writerIndex = this.markedWriterIndex;
return this;
}
5.16 writeByte(byte b)
writeByte (byte b),表示一次寫入一個(gè)字節(jié),writerIndex++。
源碼:
public ByteBuf writeByte(int value) {
this.ensureAccessible();
this.ensureWritable0(1);
//writerIndex指針自增
this._setByte(this.writerIndex++, value);
return this;
}
5.17 writeBytes(byte[] src)
writeBytes (byte [] src),表示寫入一個(gè)字節(jié)數(shù)組,writerIndex=writerIndex+src.length。
源碼:
public ByteBuf writeBytes(byte[] src) {
this.writeBytes((byte[])src, 0, src.length);
return this;
}
public ByteBuf writeBytes(byte[] src, int srcIndex, int length) {
this.ensureAccessible();
this.ensureWritable(length);
this.setBytes(this.writerIndex, src, srcIndex, length);
this.writerIndex += length; //writerIndex指針增加
return this;
}
5.18 readByte()
readByte (),表示一次讀取一個(gè)字節(jié),readerIndex++。
源碼:
public byte readByte() {
this.checkReadableBytes0(1);
int i = this.readerIndex;
byte b = this._getByte(i);//讀取數(shù)據(jù)
this.readerIndex = i + 1; //指針自增
return b;
}
5.19 readBytes(byte[] dst)
readBytes (byte [] dst),表示把 ByteBuf 里面的數(shù)據(jù)讀取到 dst,readerIndex=readerIndex+dst.length。
源碼:
public ByteBuf readBytes(byte[] dst) {
this.readBytes((byte[])dst, 0, dst.length);
return this;
}
public ByteBuf readBytes(byte[] dst, int dstIndex, int length) {
this.checkReadableBytes(length);
this.getBytes(this.readerIndex, dst, dstIndex, length);//往目標(biāo)dst里面寫數(shù)據(jù)
this.readerIndex += length;//指針增加
return this;
}
讀寫 API 類似的 API 還有 getBytes、getByte () 與 setBytes ()、setByte () 系列
區(qū)別就是 get/set 不會(huì)改變讀寫指針,而 read/write 會(huì)改變讀寫指針,這點(diǎn)在解析數(shù)據(jù)的時(shí)候千萬要注意。
6. 小結(jié)
本節(jié)內(nèi)容主要講解了 ByteBuf,需要掌握的內(nèi)容如下:
- 理解并且牢記 ByteBuf 結(jié)構(gòu)圖;
- 基于讀寫指針、容量、最大可擴(kuò)容容量,衍生出一系列的讀寫方法,要注意 read/write 與 get/set 的區(qū)別;