前言:

    Netty提供了自己的ByteBuffer操作类,名字叫做ByteBuf。相比较而言,ByteBuf的操作相对简单些,也提供了更多的方法来操作字节数组。

1.ByteBuf的基本参数

    ByteBuf是一个基本接口,只提供方法,关于其基本参数我们可以参考其最重要的抽象实现类AbstractByteBuf

public abstract class AbstractByteBuf extends ByteBuf {
    // 读索引(该值之前的数据都是已读过的,可读值范围为writeIndex-readerIndex)
    int readerIndex;
    // 写索引(该值之前的数据都是已经写过的,可写值范围为capacity-writerIndex)
    int writerIndex;
    // 以下两个是标记读写位置,同reset配套使用
    private int markedReaderIndex;
    private int markedWriterIndex;
    // 最大容量,最大不会超过这个
    private int maxCapacity;
    // 还有一个获取容量的方法,由于会有不同类型的实现,
    // 所以提供一个方法来获取容量,而不是直接一个int值
    public abstract int capacity()
}

总结下来就是4个主要的参数 readerIndex、writerIndex、capacity、maxCapacity。他们之间的关系图如下:

android ByteBuffer 写入到文件 bytebuffer api_数据

 

相比较ByteBuffer而言,多了writeIndex和maxCapacity。我们不再需要flip方法来切换读写模式;同时提供maxCapacity,当写数据达到capacity时,可以扩容一倍,最大不超过maxCapacity。

0-readerIndex:已读过的数据,不会再读;

readerIndex-writerIndex:可读的数据,一直到writeIndex为止;

writerIndex-capacity:可写的数据,一直到capacity为止;

capacity-maxCapacity:可扩容段,最大不超过maxCapacity;

后续的方法也是围绕这几个属性来操作的。

2.ByteBuf的基本API分类

public abstract class ByteBuf implements ReferenceCounted, Comparable<ByteBuf> {
 
    // 写数据 set类方法,支持从某index后开始写数据
    public abstract ByteBuf setBoolean(int index, boolean value);
	public abstract ByteBuf setByte(int index, int value);
    public abstract ByteBuf setChar(int index, int value);
    public abstract ByteBuf setShort(int index, int value);
    public abstract ByteBuf setMedium(int index, int value);
    public abstract ByteBuf setInt(int index, int value);
    public abstract ByteBuf setLong(int index, long value);
    public abstract ByteBuf setFloat(int index, float value);
    public abstract ByteBuf setDouble(int index, double value);
    
    // 写数据 write类方法,从当前writerIndex进行写数据,会增长writerIndex
    public abstract ByteBuf writeInt(int value);
    public abstract ByteBuf writeLong(long value);
    ...
        
    // 写入字节数组数据
    public abstract ByteBuf setBytes(int index, ByteBuf src);
    ...
    public abstract int setCharSequence(int index, CharSequence sequence, Charset charset);
    
    // 读数据 支持从index开始读取数据
    public abstract boolean getBoolean(int index);
    public abstract byte  getByte(int index);
    public abstract int   getInt(int index);
    ...
        
    // 读数据 从readerIndex开始读取,会增长readerIndex值
    public abstract byte  readByte();
    public abstract int   readInt();
    
    // 获取位置类方法
    // 读取可读位置
    public abstract int readerIndex();
    public abstract ByteBuf readerIndex(int readerIndex);
    // 获取可写入位置
    public abstract int writerIndex(); 
    public abstract ByteBuf writerIndex(int writerIndex);
    // 设置读取和写入位置
    public abstract ByteBuf setIndex(int readerIndex, int writerIndex); 
    // 剩余可读字节数
    public abstract int readableBytes(); 
    // 剩余可写字节数
    public abstract int writableBytes(); 
    public abstract int maxWritableBytes();
    public abstract boolean isReadable();
    public abstract boolean isReadable(int size);
    public abstract boolean isWritable();
    public abstract boolean isWritable(int size);
    public abstract ByteBuf ensureWritable(int minWritableBytes);
    public abstract int ensureWritable(int minWritableBytes, boolean force);
    public abstract ByteBuf markReaderIndex(); 
    public abstract ByteBuf resetReaderIndex();
    public abstract ByteBuf markWriterIndex(); 
    public abstract ByteBuf resetWriterIndex();
    
    // 其他获取类方法
    public abstract int capacity(); // 容量
    public abstract ByteBuf capacity(int newCapacity);
    public abstract int maxCapacity(); // 最大容量
    public abstract int capacity(); // 容量
    public abstract ByteBuf capacity(int newCapacity);
    public abstract int maxCapacity(); // 最大容量
    // 分配器,用于创建 ByteBuf 对象
    public abstract ByteBufAllocator alloc(); 

    // 字节序,即大小端。
    public abstract ByteOrder order(); 
    public abstract ByteBuf order(ByteOrder endianness);
    // 获得被包装ByteBuf 对象。
    public abstract ByteBuf unwrap(); 
    // 是否 NIO Direct Buffer
    public abstract boolean isDirect(); 
    // 是否为只读 Buffer
    public abstract boolean isReadOnly();
}

以上方法都是属于ByteBuf的基本API,也比较简单,笔者不再赘述,大家可以看下其注解。

// 舍弃已读数据,本质上是释放已读数据空间
public abstract ByteBuf discardReadBytes();
// 与discardReadBytes类似,但是该方法并不释放全部的已读数据
public abstract ByteBuf discardSomeReadBytes();
// 清空数据,本质上是将readerIndex和writerIndex重置为0
public abstract ByteBuf clear();

3.AbstractByteBuf基本实现

    作为ByteBuf的最基础的抽象实现,基本所有的其他实现类都继承了该抽象类。我们来简单看下其实现

public abstract class AbstractByteBuf extends ByteBuf {
    int readerIndex;
    int writerIndex;
    private int markedReaderIndex;
    private int markedWriterIndex;
    private int maxCapacity;

    // 构造方法只需要提供maxCapacity即可
    protected AbstractByteBuf(int maxCapacity) {
        checkPositiveOrZero(maxCapacity, "maxCapacity");
        this.maxCapacity = maxCapacity;
    }
    
    // 是否可读,就是比较两个index,若readerIndex小于writerIndex说明中间还有数据可读
    public boolean isReadable() {
        return writerIndex > readerIndex;
    }
    
    // 是否有数据可写,则比较writerIndex与capacity是否有空隙
    public boolean isWritable() {
        return capacity() > writerIndex;
    }
    
    // 舍弃已读数据空间
    public ByteBuf discardReadBytes() {
        if (readerIndex == 0) {
            ensureAccessible();
            return this;
        }

        if (readerIndex != writerIndex) {
            setBytes(0, this, readerIndex, writerIndex - readerIndex);
            // 重置writerIndex为writerIndex-readerIndex
            writerIndex -= readerIndex;
            adjustMarkers(readerIndex);
            // readerIndex设置为0
            readerIndex = 0;
        } else {
            ensureAccessible();
            adjustMarkers(readerIndex);
            writerIndex = readerIndex = 0;
        }
        return this;
    }
    
    // 释放一部分已读数据空间
    public ByteBuf discardSomeReadBytes() {
        if (readerIndex > 0) {
            if (readerIndex == writerIndex) {
                ensureAccessible();
                adjustMarkers(readerIndex);
                writerIndex = readerIndex = 0;
                return this;
            }

            // 重点在这里,如果已读数据已经超过capacity容量的一半了,则删除一部分已读数据空间
            if (readerIndex >= capacity() >>> 1) {
                setBytes(0, this, readerIndex, writerIndex - readerIndex);
                writerIndex -= readerIndex;
                adjustMarkers(readerIndex);
                readerIndex = 0;
                return this;
            }
        }
        ensureAccessible();
        return this;
    }
    
    // 写数据时,为保证数据可完整写入,及时进行扩容
    final void ensureWritable0(int minWritableBytes) {
        final int writerIndex = writerIndex();
        final int targetCapacity = writerIndex + minWritableBytes;
        if (targetCapacity <= capacity()) {
            ensureAccessible();
            return;
        }
        if (checkBounds && targetCapacity > maxCapacity) {
            ensureAccessible();
            throw new IndexOutOfBoundsException(String.format(
                    "writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s",
                    writerIndex, minWritableBytes, maxCapacity, this));
        }

        // 重点在这里,若需要的空间不超过maxCapacity,则先将当前capacity扩容1倍
        final int fastWritable = maxFastWritableBytes();
        int newCapacity = fastWritable >= minWritableBytes ? writerIndex + fastWritable
                : alloc().calculateNewCapacity(targetCapacity, maxCapacity);

        // Adjust to the new capacity.
        capacity(newCapacity);
    }
    
    // read类方法会从当前readerIndex读取4字节,按照ByteOrder拼装成int类型值
    // readerIndex会增加
    public int readInt() {
        checkReadableBytes0(4);
        int v = _getInt(readerIndex);
        readerIndex += 4;
        return v;
    }
    
    // get类型方法时从指定index开始读取4字节,按照ByteOrder进行int数据封装
    // 并不会增加readerIndex
    public int getInt(int index) {
        checkIndex(index, 4);
        return _getInt(index);
    }

    protected abstract int _getInt(int index);
    
    @Override
    public ByteBuf clear() {
        // 清空方法即将readerIndex和writerIndex都设置为0
        // 需要注意的是原来的数据依然存在,若再次设置不正确的writerIndex可能会读到脏数据
        readerIndex = writerIndex = 0;
        return this;
    }
    ...
}

有关于discardReadBytes()方法可以参考下图(图片来自网络):

android ByteBuffer 写入到文件 bytebuffer api_netty_02

 方法实在是太多了,笔者不再多介绍。相对而言都是比较简单的实现。只是谨记4个属性的使用,基本没啥困难的。

4.ByteBuf相关实现类

android ByteBuffer 写入到文件 bytebuffer api_写数据_03

 这些实现的底层原理不太一样,依据不同的实现方式我们可以划分为以下几个类别:

4.1 内存类型:

    1)HeapByteBuf,底层为JVM中的字节数组,申请和释放效率较高。进行socket IO传输时,会多一次内存复制。

    2)DirectByteBuf,直接缓冲区,为堆外内存分配,由操作系统分配(使用Unsafe分配),申请和释放会慢于HeapByteBuf。进行Socket IO传输时,会较HeapByteBuf少一次内存复制。

4.2 Unsafe分配:

    Unsafe是一个蛮神奇的东东,它可以直接操作内存地址,对地址的内存地址内容进行操作,所以相较而言,使用Unsafe API效率会更高点,但是,官方不建议直接使用Unsafe,会有很多不可控因素。

    1)UnpooledUnsafeHeapByteBuf,使用Unsafe来操作底层数组

    2)UnpooledHeapByteBuf,使用JVM提供的数组基本API来进行操作

4.3 对象池分配:

    对象池,顾名思义,与其他池类的产品一样,对象的获取是从池中获取的,对象的释放也只是归还到池中,以减少对象创建的开销。

    1)UnpooledHeapByteBuf ,不使用池来获取对象,直接创建对应ByteBuf

    2)PooledHeapByteBuf,使用对象池来获取对象

本文中就先介绍下Unpooled相关实现,关于pooled对象池的相关实现,则专门用一篇博客来介绍。

5.UnpooledHeapByteBuf

// Big endian Java heap buffer implementation
public class UnpooledHeapByteBuf extends AbstractReferenceCountedByteBuf {
    // 分配器,一般都是使用该分配器来分配ByteBuf,后续单独介绍
    private final ByteBufAllocator alloc;
    // 真正的字节数组,数据存放地
    byte[] array;
    // 临时的ByteBuffer
    private ByteBuffer tmpNioBuf;
    
    // 构造方法
    public UnpooledHeapByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
        super(maxCapacity);

        if (initialCapacity > maxCapacity) {
            throw new IllegalArgumentException(String.format(
                    "initialCapacity(%d) > maxCapacity(%d)", initialCapacity, maxCapacity));
        }

        this.alloc = checkNotNull(alloc, "alloc");
        setArray(allocateArray(initialCapacity));
        setIndex(0, 0);
    }
    
    // 与ByteBuffer的转换
    public ByteBuffer nioBuffer(int index, int length) {
        ensureAccessible();
        return ByteBuffer.wrap(array, index, length).slice();
    }
    
    // 获取指定index的字节数据
    public byte getByte(int index) {
        ensureAccessible();
        return _getByte(index);
    }

    @Override
    protected byte _getByte(int index) {
        // 具体参见下面,比较简单
        return HeapByteBufUtil.getByte(array, index);
    }
    
    // 获取指定位置int类型数据
    public int getInt(int index) {
        ensureAccessible();
        return _getInt(index);
    }

    @Override
    protected int _getInt(int index) {
        return HeapByteBufUtil.getInt(array, index);
    }
    ...
    // 其他的get类型 set类型方法操作基本都是类似的
    
    // 这个方法比较重要,设置容量,设计到扩容和缩容
    public ByteBuf capacity(int newCapacity) {
        checkNewCapacity(newCapacity);
        byte[] oldArray = array;
        int oldCapacity = oldArray.length;
        if (newCapacity == oldCapacity) {
            return this;
        }

        int bytesToCopy;
        if (newCapacity > oldCapacity) {
            bytesToCopy = oldCapacity;
        } else {
            trimIndicesToCapacity(newCapacity);
            bytesToCopy = newCapacity;
        }
        byte[] newArray = allocateArray(newCapacity);
        System.arraycopy(oldArray, 0, newArray, 0, bytesToCopy);
        setArray(newArray);
        freeArray(oldArray);
        return this;
    }
}

// HeapByteBufUtil
final class HeapByteBufUtil {
    // 比较简单,就是获取数组指定index数据
    static byte getByte(byte[] memory, int index) {
        return memory[index];
    }
    
    // 大端序列,获取index后4字节内容,拼接成int数据
    static int getInt(byte[] memory, int index) {
        return  (memory[index]     & 0xff) << 24 |
                (memory[index + 1] & 0xff) << 16 |
                (memory[index + 2] & 0xff) <<  8 |
                memory[index + 3] & 0xff;
    }
    
    // 拼接数据,与HeapByteBuffer基本一样的处理方式
    static void setInt(byte[] memory, int index, int value) {
        memory[index]     = (byte) (value >>> 24);
        memory[index + 1] = (byte) (value >>> 16);
        memory[index + 2] = (byte) (value >>> 8);
        memory[index + 3] = (byte) value;
    }
}

总结:UnpooledHeapByteBuf,与JDK中的HeapByteBuffer基本一样,都是对数组的一阵操作。代码也比较简单,这里不再赘述。

6.UnpooledUnsafeHeapByteBuf

// 直接继承了UnpooledHeapByteBuf,我们直接看其重写了哪些实现
public class UnpooledUnsafeHeapByteBuf extends UnpooledHeapByteBuf {
    
    public byte getByte(int index) {
        checkIndex(index);
        return _getByte(index);
    }

    @Override
    protected byte _getByte(int index) {
        return UnsafeByteBufUtil.getByte(array, index);
    }
    
    public int getInt(int index) {
        checkIndex(index, 4);
        return _getInt(index);
    }

    @Override
    protected int _getInt(int index) {
        return UnsafeByteBufUtil.getInt(array, index);
    }
    ...
}

// UnsafeByteBufUtil
final class UnsafeByteBufUtil {
 
    static byte getByte(byte[] array, int index) {
        return PlatformDependent.getByte(array, index);
    }
    
    static int getInt(byte[] array, int index) {
        if (UNALIGNED) {
            int v = PlatformDependent.getInt(array, index);
            return BIG_ENDIAN_NATIVE_ORDER ? v : Integer.reverseBytes(v);
        }
        return PlatformDependent.getByte(array, index) << 24 |
               (PlatformDependent.getByte(array, index + 1) & 0xff) << 16 |
               (PlatformDependent.getByte(array, index + 2) & 0xff) <<  8 |
               PlatformDependent.getByte(array, index + 3) & 0xff;
    }
}

// PlatformDependent
public final class PlatformDependent {
    public static byte getByte(byte[] data, int index) {
        // 后续直接调用的	
        // Unsafe.getByte(data, BYTE_ARRAY_BASE_OFFSET + index);
        return PlatformDependent0.getByte(data, index);
    }
}

总结:UnpooledUnsafeHeapByteBuf直接继承了UnpooledHeapByteBuf。而其复写的方法主要就是getByte、getInt等方法。

UnpooledHeapByteBuf采用数组的操作来获取具体的byte、int值;

UnpooledUnsafeHeapByteBuf则采用Unsafe来获取对应address(对象内存地址)的byte、int值,相较而言,效率会更高点。

7.UnpooledDirectByteBuf

// A NIO {@link ByteBuffer} based buffer.
public class UnpooledDirectByteBuf extends AbstractReferenceCountedByteBuf {

    // ByteBuf分配器
    private final ByteBufAllocator alloc;

    // 针对DirectByteBuf而言,其直接采用DirectByteBuffer的API来操作的,后续会看到
    ByteBuffer buffer; // accessed by UnpooledUnsafeNoCleanerDirectByteBuf.reallocateDirect()
    private ByteBuffer tmpNioBuf;
    // 容量
    private int capacity;
    private boolean doNotFree;
    
    // 构造方法
    public UnpooledDirectByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
        super(maxCapacity);
        ObjectUtil.checkNotNull(alloc, "alloc");
        checkPositiveOrZero(initialCapacity, "initialCapacity");
        checkPositiveOrZero(maxCapacity, "maxCapacity");
        if (initialCapacity > maxCapacity) {
            throw new IllegalArgumentException(String.format(
                    "initialCapacity(%d) > maxCapacity(%d)", initialCapacity, maxCapacity));
        }

        this.alloc = alloc;
        // 直接将通过ByteBuffer获取的直接内存buffer赋值给当前buffer对象,供后续方法使用
        setByteBuffer(allocateDirect(initialCapacity), false);
    }
    
    // 采用的就是ByteBuffer来获取直接内存Buffer
    protected ByteBuffer allocateDirect(int initialCapacity) {
        return ByteBuffer.allocateDirect(initialCapacity);
    }
    
    public byte getByte(int index) {
        ensureAccessible();
        return _getByte(index);
    }

    @Override
    protected byte _getByte(int index) {
        // 直接调用DirectByteBuffer.get方法
        return buffer.get(index);
    }
    
    public int getInt(int index) {
        ensureAccessible();
        return _getInt(index);
    }

    @Override
    protected int _getInt(int index) {
        // 同样也是直接调用DirectByteBuffer.getInt方法
        return buffer.getInt(index);
    }
    ...
}

总结:UnpooledDirectByteBuf没有任何特殊之处,基本将所有的请求全部转发给JDK中DirectByteBuffer来执行,相对比较简单,笔者不再赘述。