亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

首頁 慕課教程 Netty 教程 Netty 教程 Netty ByteBuf傳輸載體

Netty ByteBuf 傳輸載體

1. 前言

在 Netty 里面的數據讀寫是以 ByteBuf 為單位進行交互的,ByteBuf 是一個字節容器,如果了解過 NIO 的同學應該知道,在 NIO 里面也有類型的數據載體 ByteBuffer。

2. 學習目的

熟悉掌握 ByteBuf 的原理及 API,則可以自定義通信協議,并且使用 ByteBuf、序列化等技術實現通信協議,并且有效解決拆包和粘包問題(后面章節會詳細分析)。

3. ByteBuf 結構

ByteBuff 的結構主要由四個部分組成,廢棄字節、可讀字節、可寫字節、可擴容字節,具體結構圖如下所示:

圖片描述

通過以上的結構圖,我們可以看出它其實就是一個數組,在寫數據和讀數據的時候分別維護兩個指針的移動,分別是 readerIndex 和 writerIndex,在 readerIndex 和 writerIndex 之間的數據是有效可讀數據。具體分析如下所示:

  1. ByteBuf 的四個核心屬性,分別是 readerIndex(可讀指針位置)、writerIndex(可寫指針位置)、capacity(初始化容量值)、maxCapacity(最大容量值)。其中 readerIndex 和 writerIndex 之間的數據是 ByteBuf 的主體數據;
  2. 讀取數據的時候,readerIndex 遞增,一旦 readerIndex 等于 writerIndex 則表示該容器沒有數據可讀了。writerIndex-readerIndex 表示有效可讀數據的長度;
  3. 寫數據的時候,writerIndex 遞增,一旦 writerIndex 等于 capacity 表示容器已經滿了,ByteBuf 不能再寫數據了,capacity-writerIndex 表示容器還可以寫入的數據長度;
  4. 當向 ByteBuf 寫數據的時候,如果容量不足,那么這個時候可以進行擴容,直到 capacity 擴容到 maxCapacity,超過 maxCapacity 就會報錯;
  5. 總結,readerIndex<=writerIndex<=capacity<=maxCapacity。

4. 核心 API

方法 描述
capacity() 容量
maxCapacity() 最大容量(當容量最大時,還可以擴容)
readableBytes() 可讀字節數
isReadable() 是否可讀
writableBytes() 可寫字節數
isWritable() 是否可寫
maxWritableBytes() 最大可寫字節數
readerIndex() 讀指針
readerIndex(int) 重置讀指針為某個位置
writerIndex() 寫指針
writeIndex(int) 重置寫指針為某個位置
markReaderIndex() 保存當前讀指針
resetReaderIndex() 回歸之前保存的讀指針
markWriterIndex() 保存當前寫指針
resetWriterIndex 回歸之前保存的寫指針
writeByte(int i) 寫一個字節
writeBytes(byte[] bytes) 寫一個字節數組
readByte() 讀一個字節
readByte(byte[] bytes) 讀一個字節數組(并往參數 bytes 里存放)

以上是 ByteBuf 的核心的 API,很多時候,在編程的時候直接操作 ByteBuf 可能會相對的繁瑣,所以不會直接手工調用這些 API,而是通過封裝編碼和解碼器的方式進行使用,但是編碼、解碼底層就是通過 ByteBuf 去實現的。

5. 核心 API 詳解

5.1 capatiy()

表示 ByteBuf 可以寫入多少個字節,一般在初始化 ByteBuf 時就會指定。

實例:

ByteBuf byteBuf=Unpooled.buffer(10);

5.2 maxCapacity()

表示 ByteBuf 最大可以支持多少字節,如果當 writerIndex=capacity 時,會判斷 capacity 是否等于 maxCapacity,如果小于則擴容。

實例:

//參數1,容量值
//參數2,最大容量值
ByteBuf byteBuf = Unpooled.buffer(10,20);

5.3 readalbeBytes()

表示 ByteBuf 當前可讀的字節數,它的值等于 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 當前可寫的字節數,它的值等于 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 相等,并不代表不能繼續往 ByteBuf 寫數據了。如果發現往 ByteBuf 中寫數據寫不進去的話,Netty 會自動擴容 ByteBuf,直到擴容到底層的內存大小為 maxCapacity,而 maxWritableBytes () 就表示可寫的最大字節數,它的值等于 maxCapacity-writerIndex。

源碼:

public int maxWritableBytes() {
    return this.maxCapacity() - this.writerIndex;
}

5.8 readerIndex()

readerIndex () 表示返回當前的讀指針 readerIndex,ByteBuf 會維護一個變量 readerIndex。

源碼:

int readerIndex;
public int readerIndex() {
    return this.readerIndex;
}

5.9 readerIndex(int)

readerIndex (int) 表示設置讀指針,比如說可以回滾到某個指針位置。

源碼:

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 () 表示返回當前的寫指針 writerIndex。

源碼:

public int writerIndex() {
    return this.writerIndex;
}

5.11 writeIndex(int)

writeIndex (int) 表示設置寫指針,比如說可以回滾到某個指針位置。

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 () 表示把當前的讀指針保存起來,其實類似數據庫事務的當前狀態標記。

源碼:

public ByteBuf markReaderIndex() {
    //使用markedReaderIndex保存當前讀指針
    this.markedReaderIndex = this.readerIndex;
    return this;
}

5.13 resetReaderIndex()

resetReaderIndex () 表示把當前的讀指針恢復到之前保存的值,類似數據庫事務回歸到某個狀態。

源碼:

public ByteBuf resetReaderIndex() {
    this.readerIndex(this.markedReaderIndex);
    return this;
}

5.14 markWriterIndex()

表示把當前的寫指針保存起來。

源碼:

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),表示一次寫入一個字節,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),表示寫入一個字節數組,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 (),表示一次讀取一個字節,readerIndex++。

源碼:

public byte readByte() {
    this.checkReadableBytes0(1);
    int i = this.readerIndex;
    byte b = this._getByte(i);//讀取數據
    this.readerIndex = i + 1; //指針自增
    return b;
}

5.19 readBytes(byte[] dst)

readBytes (byte [] dst),表示把 ByteBuf 里面的數據讀取到 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);//往目標dst里面寫數據
    this.readerIndex += length;//指針增加
    return this;
}

讀寫 API 類似的 API 還有 getBytes、getByte () 與 setBytes ()、setByte () 系列

區別就是 get/set 不會改變讀寫指針,而 read/write 會改變讀寫指針,這點在解析數據的時候千萬要注意。

6. 小結

本節內容主要講解了 ByteBuf,需要掌握的內容如下:

  1. 理解并且牢記 ByteBuf 結構圖;
  2. 基于讀寫指針、容量、最大可擴容容量,衍生出一系列的讀寫方法,要注意 read/write 與 get/set 的區別;