ByteBuf是netty用于替代nio的ByteBuffer,存储字节的数据容器,相比于ByteBuffer,ByteBuf提供了更加强大和灵活的功能。主要表现在以下几个方面
- capacity是可扩展的,不像ByteBuffer一经创建,就固定不变了。
- 有两个索引,readIndex和writeIndex,分别用于读索引和写索引,而无需像ByteBuffer那样使用flip方法。
- 方法支持链式调用
- 引用计数
- 支持池化
具体与ByteBuffer有啥区别,可以参考这篇:nio之ByteBuffer
readerIndex and writerIndex
ByteBuf通过readerIndex和writerIndex将一个buffer划分为3个区域,如图
1.1.png
discardable bytes
该区域是已经读取过的字节数据,因此称为可丢弃字节。在一个buffer刚创建时,该区域为的字节数为0,随着readerIndex的增加(读操作),该区域也会跟着扩大。可通过调用discardReadBytes()方法来回收该区域。其实就是将readerIndex和writerIndex的指针同时往前移动,移动的数为readerIndex的值,也就是读过的字节数。通过源码更能看出来。
public ByteBuf discardReadBytes() {
if (readerIndex == 0) {
ensureAccessible();
return this;
}
if (readerIndex != writerIndex) {
setBytes(0, this, readerIndex, writerIndex - readerIndex);
writerIndex -= readerIndex;
adjustMarkers(readerIndex);
readerIndex = 0;
} else {
ensureAccessible();
adjustMarkers(readerIndex);
writerIndex = readerIndex = 0;
}
return this;
}
下面这2张图为回收之前和之后的
1.2.png
readable bytes
该区域是可读取的字节数据,buffer中以read和skip开头的方法被调用都会增加readerIndex的值。若读取的方法参数是另一个ByteBuf,那么目标ByteBuf的writerIndex也会随着增加。
writable bytes
该区域是可写的,buffer中还未被使用的区域。buffer中以write开头的方法被调用都会增加writerIndex的值,若写的方法参数是另一个buffer,那么目标ByteBuf的readerIndex的值。
ByteBuffer使用模式
ByteBuf与ByteBuffer一样,提供了heap buffer和direct buffer。作用与ByteBuffer类似,这边不再解释了,可通过hasArray方法判断buffer是heap的还是direct的,除了这两者,ByteBuf还提供另一种模式,组合模式CompositeByteBuf。该模式的buffer主要是组合多个ByteBuf对象,基本的使用方式如:
ByteBuf byteBuf1 = Unpooled.directBuffer();
byteBuf1.writeBytes("hello".getBytes());
ByteBuf byteBuf2 = Unpooled.wrappedBuffer("world".getBytes());
CompositeByteBuf compositeByteBuf = Unpooled.compositeBuffer();
compositeByteBuf.addComponent(byteBuf1);
compositeByteBuf.addComponent(byteBuf2);
for (int i = 0; i < compositeByteBuf.numComponents(); i++) {
ByteBuf byteBuf = compositeByteBuf.component(i);
System.out.println(byteBuf.toString(CharsetUtil.UTF_8));
}
这种方式对自定义消息体协议有很大的帮组作用,比如http协议中,消息体由header和body两部分组成,那么对于组合buffer来说,就可以针对这两部分分别写入和读取,极大简化了对消息体的解析。
对于一个buffer的创建,一般推荐使用Unpooled这个类提供的静态方法来创建,而不是直接通过ByteBuf实现类的构造方法来创建。从该类的名字就可看出创建出来的buffer是未池化的。创建buffer的方式主要有3种,一种是直接分配新的空间;一种是wrap包装已存在的数据,该种方式对buffer或者是包装的对象的修改都是对对方可见的,也就是会直接影响到对方;还有一种是copy现有的数据,这种方式相当于两份独立的数据,互不干扰。
ByteBuf的其他作用如引用计数、池化,这些在本文先不介绍,放在后面与netty的其他组件一起介绍。本文只是介绍了ByteBuf的基础内容,具体的如何使用以及其他内容可参考netty的api。
网友评论