大家好,又见面了,我是你们的朋友全栈君。
与NIO中的ByteBuffer
类似,Netty中以ByteBuf
作为它的字节容器。ByteBuf
相当于ByteBuffer
的升级。有兴趣可以看一看>>之前NIO中的ByteBuffer
的文章
ByteBuffer存在一定的缺陷:
ByteBuf
维护了两个不同的索引:一个用于读取,一个用于写入。读取时,读索引递增读取的字节数。写入时,写索引会递增写入的字节数。初始时两个索引都为0。当两个索引相同时,也就是说数组中没有数据可读了。在试图读取会触发边界溢出异常。
整个ByteBuf被这两个指针最多分成三个部分,分别是可丢弃部分,可读部分和可写部分,可以用一张图直观的描述ByteBuf的结构,如下图所示:
从内存分配的角度看,ByteBuf可以分为两类:
从内存回收的角度看,ByteBuf可以分为两类:
以上两种类型进行两两组合就构成了多种多样的ByteBuf,供用户选择使用。
首先肯定是要创建一个ByteBuf,更确切的说法就是要申请一块内存,后续可以在这块内存中执行写入数据读取数据等等一系列的操作。
那么如何创建一个ByteBuf呢?Netty中设计了一个专门负责分配ByteBuf的接口:ByteBufAllocator。该接口有一个抽象子类和两个实现类,分别对应了用来分配池化的ByteBuf和非池化的ByteBuf。 具体的层级关系如下图所示:
Netty又为我们提供了两个工具类:Pooled、Unpooled,分类用来分配池化的和未池化的ByteBuf Unpooled类为例,提供了如下创建方法:
// 在堆上分配一个ByteBuf,并指定初始容量和最大容量
public static ByteBuf buffer(int initialCapacity, int maxCapacity) {
return ALLOC.heapBuffer(initialCapacity, maxCapacity);
}
// 在堆外分配一个ByteBuf,并指定初始容量和最大容量
public static ByteBuf directBuffer(int initialCapacity, int maxCapacity) {
return ALLOC.directBuffer(initialCapacity, maxCapacity);
}
// 使用包装的方式,将一个byte[]包装成一个ByteBuf后返回
public static ByteBuf wrappedBuffer(byte[] array) {
if (array.length == 0) {
return EMPTY_BUFFER;
}
return new UnpooledHeapByteBuf(ALLOC, array, array.length);
}
// 返回一个组合ByteBuf,并指定组合的个数
public static CompositeByteBuf compositeBuffer(int maxNumComponents){
return new CompositeByteBuf(ALLOC, false, maxNumComponents);
}
ALLOC实际是一个ByteBufAllocator
private static final ByteBufAllocator ALLOC = UnpooledByteBufAllocator.DEFAULT;
ByteBufAllocator是一个专门负责ByteBuf分配的接口,对应的Unpooled实现类就是UnpooledByteBufAllocator。在UnpooledByteBufAllocator类中可以看到UnpooledByteBufAllocator.DEFAULT变量是一个final类型的静态变量
public static final UnpooledByteBufAllocator DEFAULT =
new UnpooledByteBufAllocator(PlatformDependent.directBufferPreferred());
ByteBuf中定义了两类方法可以往ByteBuf中写入内容:writeXX() 和 setXX()。setXX是替换指定位置的值,而writeXX是想当前写指针写入数据后递增指针。
比如:setByte(int index, int value)
表示将指定位置上的内容修改为指定的byte的值高24位上的内容将被丢弃。
why?Java中一个int占4个byte字节,即32bit(位),所以就会存在当写入一个byte时,参数用int来传值时,高24位的内容会被丢弃。这是因为一个int被拆成了4个byte,而写入一个byte到指定的位置时,那么其余的3个byte就被丢弃了,也就是丢弃的24位。
跟写操作一样,ByteBuf的读操作也有两种方法,分别是getXX()和readXX()。
public void test1() {
CompositeByteBuf message = Unpooled.compositeBuffer();
ByteBuf header = Unpooled.buffer();
ByteBuf body = Unpooled.directBuffer();
message.addComponents(header, body);
for(ByteBuf buf:message) {
System.out.println(buf.toString());
}
int length = message.readableBytes();
byte[] array = new byte[length];
//将字节读到array数组中
message.getBytes(message.readerIndex(), array);
}
随机访问索引和数据一样,ByteBuf的索引是从0开始,因此遍历非常容易
for(int i=0; i<buffer.capacity(); i++){
byte b = buffer.getByte(i);
System.out.println((char)b)
}
注意:get、set之类方法不会改变读写索引的位置。
顺序访问索JDK的ByteBuffer只有一个索引,这也是为什么必须调用flip()方法在读模式和写模式切换。
ByteBuf buf = Unpooled.buffer();
String str = "four you";
buf.writeBytes(str.getBytes());
System.out.println((char)buf.readByte());
int n = (char)buf.readerIndex();
System.out.println(n );
ByteBuf
的可读字节分段存储了实际的数据,新分配的、包装的或者复制的缓冲区默认的读写索引都为0,任何一read、skip开头的操作都会使readerIndex变化,当然write相关的方法一回事writerIndex会被增加。
ByteBuf buf = Unpooled.buffer();
String str = "four you";
buf.writeBytes(str.getBytes());
System.out.println(buf.writerIndex());
buf.skipBytes(3);
buf.writeByte('a');
int n = buf.readerIndex();
int m = buf.writerIndex();
System.out.println("readerIndex:"+n+"\nwriterIndex:"+m);
查找的例子:
ByteBuf buf = Unpooled.buffer();
String str = "four you";
buf.writeBytes(str.getBytes());
int n = buf.indexOf(0,buf.capacity()-1,(byte)'r');
System.out.println("readerIndex:"+n);
派生缓冲区 在ByteBuf中有一些方法slice、order等方法会返回一个新的ByteBuf实例,它具有自己的读写索引和标记索引,但是内部存储的数组是共享的。
ByteBuf buffer = Unpooled.copiedBuffer("four you", CharsetUtil.UTF_8);
ByteBuf bufCopy = buffer.slice(0,3);
System.out.println(bufCopy.toString(CharsetUtil.UTF_8));
//更新索引0处的字节
buffer.setByte(0,(byte)'j');
System.out.println(bufCopy.toString(CharsetUtil.UTF_8));
结果是打印出来的不一样,说明数据是共享的。
ByteBuff复制 如果真的需要一个真实的复制品,可以使用copy方法。
ByteBuf buffer = Unpooled.copiedBuffer("four you", CharsetUtil.UTF_8);
ByteBuf bufCopy = buffer.copy(0,3);
System.out.println(bufCopy.toString(CharsetUtil.UTF_8));
buffer.setByte(0,(byte)'j');
System.out.println(bufCopy.toString(CharsetUtil.UTF_8));
打印出的结果一样。说明数据是独立的。
下一篇:https://blog.csdn.net/TheLudlows/article/details/79532899
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/193324.html原文链接:https://javaforall.cn