目录
一、ByteBuf介绍
二、ByteBuf创建
1.池化创建 ByteBufAllocator
2.Unpooled (非池化)创建ByteBuf
3.ByteBufUtil 操作ByteBuf
三、读取ByteBuf数据
1.get方法
2.read方法
3.set方法
4.write方法
5.索引管理
6.索引查找
7.ByteBuf复制
8.其他方法
本篇主要介绍ByteBuf的常用操作API,Netty性能的卓越表现离不开ByteBuf的巧妙设计,它提供了一系列的缓冲区读写方法,另外为了方便起见还特定义了ByteBufUtil,提供了比较丰富的操作ByteBuf方法。
为了提高性能,Netty重新设计了字节缓冲区ByteBuf,类似Java Nio的ByteBuffer,但工作方式略有区别,比后者更加灵活、高效。
ByteBuf有几个重要属性:
capacity:容量;
0:缓冲区开始位置;
readIndex:下一个读位置;
writeIndex:下一个写位置;
一个ByteBuf对象即可像byte数组一样工作,又可以像IO字节流一样工作。当前的可读数据区是[readIndex,writeIndex);可写区是[writeIndex,capacity);而[0,readIndex)区间的字节是可废弃数据(Discardable),如下图所示:
+-------------------+------------------+------------------+
| discardable bytes | readable bytes | writable bytes |
| | (CONTENT) | |
+-------------------+------------------+------------------+
| | | |
0 <= readerIndex <= writerIndex <= capacity
基本的操作包括
1.从ByteBuf中读取数据,从readerIndex开始向后读取需要的字节数,readerIndex随之改变;
2.向ByteBuf中写数据,从writeIndex开始向后写数据,同时writeIndex随之改变;
3.删除ByteBuf已读数据,从[0,readerIndex)为已读完的数据,为了节省空间需要释放内存,释放示意图如下:
* BEFORE discardReadBytes()
*
* +-------------------+------------------+------------------+
* | discardable bytes | readable bytes | writable bytes |
* +-------------------+------------------+------------------+
* | | | |
* 0 <= readerIndex <= writerIndex <= capacity
*
*
* AFTER discardReadBytes()
*
* +------------------+--------------------------------------+
* | readable bytes | writable bytes (got more space) |
* +------------------+--------------------------------------+
* | | |
* readerIndex (0) <= writerIndex (decreased) <= capacity
4.清空ByteBuf数据,不需要清空数据,通过移动两个指针readerIndex和writeIndex,指向0即可。
* BEFORE clear()
*
* +-------------------+------------------+------------------+
* | discardable bytes | readable bytes | writable bytes |
* +-------------------+------------------+------------------+
* | | | |
* 0 <= readerIndex <= writerIndex <= capacity
*
*
* AFTER clear()
*
* +---------------------------------------------------------+
* | writable bytes (got more space) |
* +---------------------------------------------------------+
* | |
* 0 = readerIndex = writerIndex <= capacity
获取ByteBufAllocator,通过以下两种方式获取
//方法1
Channel channel = ...;
ByteBufAllocator allocator = channel.alloc();
//方法2
ChannelHandlerContext ctx = ...;
ByteBufAllocator allocator2 = ctx.alloc();
ByteBufAllocator中创建byteBuf的方法如下
方法名称 | 方法描述 |
buffer() buffer(int) buffer(int, int) |
Return a ByteBuf with heap-based or direct data storage |
heapBuffer() heapBuffer(int) heapBuffer(int, int) |
Return a ByteBuf with heap-based storage. |
directBuffer() directBuffer(int) directBuffer(int, int) |
Return a ByteBuf with direct storage. |
compositeBuffer() compositeBuffer(int) heapCompositeBuffer() heapCompositeBuffer(int) directCompositeBuffer() directCompositeBuffer(int) |
Return a CompositeByteBuf that can be expanded by adding heapbased or direct buffers. |
ioBuffer() |
Return a ByteBuf that will be used for I/O operations on a socket. |
当未引用 ByteBufAllocator 时,上面的方法无法访问到 ByteBuf。对于这个用例 Netty 提供一个实用工具类称为 Unpooled,,它提供了静态辅助方法来创建非池化的 ByteBuf 实例,方法如下:
方法名称 | 方法描述 |
buffer() buffer(int) buffer(int, int) |
Returns an unpooled ByteBuf with heap-based storage |
directBuffer() directBuffer(int) directBuffer(int, int) |
Returns an unpooled ByteBuf with direct storage |
wrappedBuffer() |
Returns a ByteBuf, which wraps the given data. |
copiedBuffer() |
Returns a ByteBuf, which copies the given data |
get类似数组操作,不会改变读索引readIndex,read会改变读索引readIndex。
方法名称 | 方法描述 |
getBoolean(int) |
返回当前索引的 Boolean 值 |
getByte(int) getUnsignedByte(int) |
返回当前索引的(无符号)字节 |
getMedium(int) getUnsignedMedium(int) |
返回当前索引的 (无符号) 24-bit 中间值 |
getInt(int) getUnsignedInt(int) |
返回当前索引的(无符号) 整型 |
getLong(int) getUnsignedLong(int) |
返回当前索引的 (无符号) Long 型 |
getShort(int) getUnsignedShort(int) |
返回当前索引的 (无符号) Short 型 |
getBytes(int, ...) |
字节 |
方法名称 | 方法描述 |
readBoolean() |
返回当前索引的Boolean值,读索引加一 |
readByte() readUnsignedByte() |
返回当前索引的(无符号)字节,读索引加一 |
readMedium() readUnsignedMedium() |
返回当前索引的 (无符号) 24-bit 中间值,读索引加3 |
readInt() readUnsignedInt() |
返回当前索引的(无符号) 整型,读索引加4 |
readLong() readUnsignedLong() |
返回当前索引的 (无符号) Long 型,读索引加8 |
readShort() readUnsignedShort() |
返回当前索引的 (无符号) Short 型,读索引加2 |
readBytes(int length) | 读取length字节数,并返回一个新创建的Buf对象,新buf的readIndex=0,writeIndex=length,源buf的readIndex+=length |
readBytes(ByteBuf dst) | 将buf的可读字节写入dst,字节数=min(src.readableBytes,dst.writableBytes),src.readIndex增加,src.writeIndex增加 |
readBytes(ByteBuf dst, int length) | 同上,指定长度 |
readBytes(ByteBuf dst, int dstIndex, int length) | 同上,但不修改dst.writeIndex |
readBytes(byte[] dst) | 数组版本,也有其他变体 |
readBytes(ByteBuffer dst) | nio buffer版本 |
readBytes(OutputStream out, int length) | ostream版本 |
readBytes(GatheringByteChannel out, int length) | nio channel版本 |
readCharSequence(int length, Charset charset) | 字符串版本 |
readBytes(FileChannel out, long position, int length) | 文件Channel版本 |
skipBytes(int length) | 跳过一些可读字节,readIndex+=length |
方法名称 | 方法描述 |
setBoolean(int, boolean) |
在指定的索引位置设置 Boolean 值 |
setByte(int, int) |
在指定的索引位置设置 byte 值 |
setMedium(int, int) |
在指定的索引位置设置 24-bit 中间 值 |
setInt(int, int) |
在指定的索引位置设置 int 值 |
setLong(int, long) |
在指定的索引位置设置 long 值 |
setShort(int, int) |
在指定的索引位置设置 short 值 |
方法名称 | 方法描述 |
writeBoolean(boolean) |
在指定的索引位置设置 Boolean 值,写索引加一 |
writeByte(int) |
在指定的索引位置设置 byte 值,写索引加一 |
writeMedium(int) |
在指定的索引位置设置 24-bit 中间 值,写索引加3 |
writeInt(int) |
在指定的索引位置设置 int 值,写索引加4 |
writeLong(long) |
在指定的索引位置设置 long 值,写索引加8 |
writeShort(int) |
在指定的索引位置设置 short 值,写索引加2 |
writeBytes(int,...) |
在当前索引写入一个Byte数组,写索引加数组长度 |
方法名称 | 方法描述 |
markReaderIndex(), markWriterIndex() |
标记读(写)索引 |
resetReaderIndex() resetWriterIndex() |
读(写)索引回到mark标记的索引值 |
readerIndex(int) writerIndex(int) |
将读(写)索引设置到指定位置 |
clear() |
可以同时设置 readerIndex 和 writerIndex 为 0。这不会清除内存中的内容 |
discardReadBytes | 抛弃已读字节,将[readerIndex, writeIndex)字节迁移到[0, readableBytes),修改readerIndex=0,writeIndex=readableBytes |
discardSomeReadBytes | 抛弃部分已读字节以节省内存,具体数量由实现来决定,以达到最高效 |
ensureWritable(int minWritableBytes) | 按需扩展容量,如果writeIndex+minWritableBytes> maxCapacity,抛出IndexOutOfBoundsException |
ensureWritable(int minWritableBytes, boolean force) |
功能同上,但不会抛出异常,参数force影响(writeIndex+minWritableBytes> maxCapacity)条件下的行为:force=true,扩充容量至maxCapacity;force=false,不做扩充。返回值只一个反映操作行为的状态码:=0,容量本来就满足,所以未扩充;=1,容量不足,但未扩充;=2,容量已扩充,满足需求;=3,容量以扩充至maxCapacity,但仍未满足需求。 |
方法名称 | 方法描述 |
bytesBefore(int index, int length, byte value) | 查询value在buf出现的位置,仅限[index,index+length)区间内查找 |
for EachByte(ByteBufProcessor.FIND_NUL) |
查找byte,返回byte的索引 |
方法名称 | 方法描述 |
slice() | 返回源buf可读字节的一个切片,新buf和源buf共享byte内存,但readIndex,writeIndex互相独立,操作buf不会影响refCnt |
duplicate() retainedDuplicate() |
制作buf的一个副本,底层共享byte内存,但readIndex,writeIndex互相独立,操作buf不会影响refCnt |
slice(int index, int length) retainedSlice(int index, int length) |
slice变体 |
retainedSlice() | 相当于slice().retain() |
copy() | 复制buf的可读字节区,新buf的readIndex=0,新buf和源buf互相独立 |
copy(int index, int length) | 新buf的readIndex=0, writeIndex=capacity=length |
方法名称 | 方法描述 |
unwrap() | 如果此对象是包装另一个ByteBuf对象生成的,返回后者,否则返回null |
isDirect() | 是否该ByteBuf是一个直接内存缓冲区 |
isReadOnly() | 是否只读 |
asReadOnly() | 返回该ByteBuf的一个只读版本 |
readerIndex(int readerIndex) | 设置readerIndex,不能<0,或>writerIndex |
writerIndex(int writerIndex) | 设置writerIndex,不能 |
setIndex(int readerIndex, int writerIndex) | 同时设置readerIndex, writerIndex |
readableBytes() | 返回可以读的字节长度=writerIndex-readerIndex |
isReadable() | 返回是否有字节可读=(writerIndex-readerIndex>0) |
isReadable(size) | 返回是否有size字节可读=(writerIndex-readerIndex>size) |
writableBytes() | 返回可以写的字节长度=capacity-writerIndex |
maxWritableBytes() | 返回最大可以写的字节长度=maxCapacity-writerIndex |
isWritable() |
返回是否可以写==(capacity - writerIndex)>0 |
isWritable(int size) | 返回是否可以写字节长度size=(capacity - writerIndex)>size |
clear() | 恢复到初始状态,相当于setIndex(0,0) |
capacity() |
返回byteBuf的容量 |
maxCapacity() |
返回byteBuf可以有的最大容量 |
hasArray() |
buf内部是否被一个byte array支撑 (heap buf才会为true) |
array() |
返回支撑的byte array,如果不支持,抛出UnsupportedOperationException |
arrayOffset() | 返回buf的0位置,在byte数组中的位置 |
hasMemoryAddress() | buf是否拥有一个指向底层内存的地址 |
memoryAddress() | 返回底层内存地址,如果不支持,抛出UnsupportedOperationException |
isContiguous() | buf是否由一整块内存支持 |
参考资料
ByteBuf的常用API总结
Netty详解之九:ByteBuf介绍