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。他们之间的关系图如下:
相比较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 {
// 写数据 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 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()方法可以参考下图(图片来自网络):
方法实在是太多了,笔者不再多介绍。相对而言都是比较简单的实现。只是谨记4个属性的使用,基本没啥困难的。
4.ByteBuf相关实现类这些实现的底层原理不太一样,依据不同的实现方式我们可以划分为以下几个类别:
4.1 内存类型:1)HeapByteBuf,底层为JVM中的字节数组,申请和释放效率较高。进行socket IO传输时,会多一次内存复制。
2)DirectByteBuf,直接缓冲区,为堆外内存分配,由操作系统分配(使用Unsafe分配),申请和释放会慢于HeapByteBuf。进行Socket IO传输时,会较HeapByteBuf少一次内存复制。
4.2 Unsafe分配:Unsafe是一个蛮神奇的东东,它可以直接操作内存地址,对地址的内存地址内容进行操作,所以相较而言,使用Unsafe API效率会更高点,但是,官方不建议直接使用Unsafe,会有很多不可控因素。具体可以参考 https://www.cnblogs.com/linghu-java/p/9686651.html
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] = (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)
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?