一、前言

  前面已经学习了Netty中传输部分,现在接着学习Netty中的ByteBuf。

二、ByteBuf

  2.1 ByteBuf API

  在网络上传输的数据形式为Byte,Java NIO提供了ByteBuffer来作为Byte容器,该类有些复杂,而Netty使用ByteBuf作为ByteBuffer的替换方案,其提供了一个更好的API,

  Netty通过ByteBuf和ByteBufHolder两个组件处理数据,而ByteBuf的API有如下优势

    · 可扩展的用户定义的缓冲区类型

    · 通过内置复合缓冲区类型实现透明零拷贝

    · 容量随着需求可扩大

    · 在读写器模式之间切换不需要调用ByteBuffer的flip()方法

    · 数据读写使用不同的索引

    · 支持方法链

    · 支持引用计数

    · 支持池

  ByteBuf维护两个不同的读索引和写索引,当读ByteBuf时,readerIndex会随着数据的读取而不断增加,同理,writerIndex也相同,对于空的ByteBuf而言,其readerIndex和writerIndex均初始化为0,如下图所示

  

  而当读取数据时,readerIndex与writerIndex相同时,表示不能再读取数据了,否则会抛出IndexOutOfBoundsException异常,以read或者write开头的方法会增加相应的索引,而set和get方法则不会,可自定义ByteBuf的大小,当超过大小时将抛出异常。

  2.2 ByteBuf使用模式

  常用的模式有如下几种:

    · 堆缓冲。将数据存储在JVM的堆空间中,使用backing array提供支持,这种模式在不使用池的情况下提供快速分配和释放。

    · 直接缓冲。通过JVM的本地调用分配内存,这可避免每次调用本地I / O操作之前(或之后)将缓冲区的内容复制到(或从)中间缓冲区。

    · 复合缓冲。呈现多个ByteBufs的聚合视图,可以添加或删除ByteBuf实例,由Netty中的CompositeByteBuf提供支持,CompositeByteBuf中的ByteBuf实例包含直接或非直接的分配。

  2.3 字节级的操作

  ByteBuf提供了很多用于修改数据的读写方法。

  1. 随机访问索引

  ByteBuf的第一个索引编号为0,最后一个编号为capacity() - 1,可使用如下代码读取ByteBuf的数据  

for (int i = 0; i < buffer.capacity(); i++) {
byte b = buffer.getByte(i);
System.out.println((char) b);
}

  值得注意的是当有索引作为参数传入方法而读取数据时,并不会改变readerIndex或者writerIndex的值。

  2. 顺序访问索引

  Netty的ByteBuf有读写两个索引,而JDK的ByteBuffer只有一个索引,因此需要使用flip方法进行读写切换,下图展示了读写索引如何将ByteBuf划分为三个区域。

  

  3. 可舍弃字节

  可舍弃的字节表示那些已经被读取的数据,可通过调用discardReadBytes() 方法舍弃并且回收该部分空间。当调用了discardReadBytes方法后,其布局如下图所示

  

  可以看到,整个容量未变,但是此时readerIndex的值变为0,可写的容量大小扩大了。

  4. 可读字节

  可读字节部分存储了真实的数据。新分配的、包装的或复制的缓冲区的readerIndex的默认值为0,以read或者skip开头的方法操作将检索或跳过当前readerIndex中的数据并增加读取的字节数,当可读字节已经用尽时,再进行读取将会抛出异常。下面代码将会读取ByteBuf中的所有数据。  

ByteBuf buffer = ...;
while (buffer.isReadable()) {
System.out.println(buffer.readByte());
}

  5. 可写字节

  可写字节部分可供写入数据,初始化的writerIndex为0,以write开头的将会从writerIndex开始写入数据,并且writerIndex会增加相应的大小。当超过ByteBuf的容量时,再写入数据时会抛出IndexOutOfBoundException异常,如下代码会随机写入一个整形。  

ByteBuf buffer = ...;
while (buffer.writableBytes() >= 4) {
buffer.writeInt(random.nextInt());
}

  6. 索引管理

  可通过调用markReaderIndex(), markWriterIndex(), resetReaderIndex(), and resetWriterIndex()方法来标记和重置readerIndex和writerIndex,也可通过调用readerIndex(int)、writerIndex(int) 方法来将readerIndex和writerIndex设置为指定值,也可通过调用clear()方法将readerIndex和writerIndex设置为0,但是并不会清空内容。

  若调用clear之前的布局如下

  

  则调用clear之后的布局如下

  

  clear()方法比discardReadBytes()方法性能更优,因为其不需要拷贝数据。

  7. 搜索操作

  有多种方法确定ByteBuf指定值的索引,如使用indexOf方法,另一种更为复杂的方法是使用ByteBufProcessor作为方法的参数,如下代码寻找\r的索引  

ByteBuf buffer = ...;
int index = buffer.forEachByte(ByteBufProcessor.FIND_CR);  

  8. 派生缓冲区

  派生缓冲提供了ByteBuf的视图,可通过如下方法创建视图duplicate()、slice()、slice(int, int)、Unpooled.unmodifiableBuffer(…)、order(ByteOrder)、readSlice(int)。

  每个方法将会返回一个新的ByteBuf实例,该实例有自己的readerIndex、writerIndex、marker索引,当修改该ByteBuf实例数据时,原始数据也将被修改。如下代码展示了使用slice方法来创建新的ByteBuf实例用法

Charset utf8 = Charset.forName("UTF-8");
ByteBuf buf = Unpooled.copiedBuffer("Netty in Action rocks!", utf8);
ByteBuf sliced = buf.slice(0, 14);
System.out.println(sliced.toString(utf8));
buf.setByte(0, (byte)'J');
assert buf.getByte(0) == sliced.getByte(0);

  其中,由于数据是共享的,对一个ByteBuf的修改对原始的ByteBuf是可见的

  下面代码展示了copy方法的使用  

Charset utf8 = Charset.forName("UTF-8");
ByteBuf buf = Unpooled.copiedBuffer("Netty in Action rocks!", utf8);
ByteBuf copy = buf.copy(0, 14);
System.out.println(copy.toString(utf8));
buf.setByte(0, (byte)'J');
assert buf.getByte(0) != copy.getByte(0);

  copy方法会重新分配新的ByteBuf,对其的修改对原始的ByteBuf不可见。

  9. 读/写操作

  get和set操作读写指定索引的数据,而不会改变索引值。read和write操作读写指定索引数据,并且会改变索引的值。

  2.4 ByteBuf分配

  1. ByteBufAllocator接口

  为减少分配和重新分配内存的开销,Netty使用ByteBufAllocator使用了池,其可分配任何类型的ByteBuf实例,Netty提供了ByteBufAllocator两种类型的实现:PooledByteBufAllocator和UnpooledByteBufAllocator,前者池化ByteBuf实例以提高性能并最小化内存碎片,后者每次调用时都返回一个新的实例。Netty默认使用PooledByteBufAllocator,但也可通过ChannelConfig改变并使用不同的分配器。

  2. 非池化缓冲

  当没有ByteBufAllocator引用时,Netty提供了Unpooled工具类,其提供了创建非池化缓冲的帮助方法,具体如下:buffer()、buffer(int initialCapacity)、buffer(int initialCapacity, int maxCapacity)、directBuffer()、directBuffer(int initialCapacity)、directBuffer(int initialCapacity, int maxCapacity)、wrappedBuffer()、copiedBuffer()等。

  3. ByteBufUtil类

  ByteBufUtil类提供了管理ByteBuf的方法,其中最有效的方法是hexdump方法,它打印ByteBuf的内容的十六进制表示,在调试时该方法非常有用。另一个方法是boolean equals(ByteBuf, ByteBuf) 方法,用来判断两个ByteBuf的相等性。

  2.5 引用计数

  引用计数是一种通过释放由对象不再被其他对象引用的对象所持有的资源来优化内存使用和性能的技术,Netty在ByteBuf和ByteBufHolder的第4版中引入了引用计数,其都实现了ReferenceCounted接口。引用计数背后的思想并不复杂,主要是跟踪指定对象的活动引用数。ReferenceCounted实现实例的初始化活动引用计数为1。只要引用计数大于0,就要保证对象不被释放,当为0时,需要被释放。当访问已经被释放的对象时会抛出IllegalReferenceCountException异常。

三、总结

  本篇博文着重讲解了ByteBuf的具体细节,以及讲解了不同的缓冲区类型,其是Netty中的核心概念,可以类比JDK中的ByteBuffer进行学习,也谢谢各位园友的观看~

【Netty】Netty之ByteBuf的更多相关文章

  1. Netty buffer缓冲区ByteBuf

    Netty buffer缓冲区ByteBuf byte 作为网络传输的基本单位,因此数据在网络中进行传输时需要将数据转换成byte进行传输.netty提供了专门的缓冲区byte生成api ByteBu ...

  2. Netty in action—Netty中的ByteBuf

    Netty in action—Netty中的ByteBuf - 日积月累 - CSDN博客 https://blog.csdn.net/yjw123456/article/details/77843 ...

  3. netty中的ByteBuf

    网络数据的基本单位总是字节.Java NIO 提供了 ByteBuffer 作为它 的字节容器,但是这个类使用起来过于复杂,而且也有些繁琐. Netty 的 ByteBuffer 替代品是 ByteB ...

  4. 网络编程Netty入门:ByteBuf分析

    目录 Netty中的ByteBuf优势 NIO使用的ByteBuffer有哪些缺点 ByteBuf的优势和做了哪些增强 ByteBuf操作示例 ByteBuf操作 简单的Demo示例 堆内和堆外内存 ...

  5. Netty 框架学习 —— ByteBuf

    概述 网络数据的基本单位总是字节,Java NIO 提供了 ByteBuffer 作为它的字节容器,但这个类的使用过于复杂.Netty 的 ByteBuf 具有卓越的功能性和灵活性,可以作为 Byte ...

  6. netty系列之:netty中的ByteBuf详解

    目录 简介 ByteBuf详解 创建一个Buff 随机访问Buff 序列读写 搜索 其他衍生buffer方法 和现有JDK类型的转换 总结 简介 netty中用于进行信息承载和交流的类叫做ByteBu ...

  7. netty系列之:不用怀疑,netty中的ByteBuf就是比JAVA中的好用

    目录 简介 ByteBuf和ByteBuffer的可扩展性 不同的使用方法 性能上的不同 总结 简介 netty作为一个优秀的的NIO框架,被广泛应用于各种服务器和框架中.同样是NIO,netty所依 ...

  8. [Netty] - Netty IN ACTION(导言)

    最近没什么事儿做,刚好看到有需要网络编程的知识,java中有NIO和IO两种不同的方式,但是NIO的编写比较麻烦,刚好找到一个成熟的网络框架Netty.接下来的一个月就准备将Netty IN ACTI ...

  9. Netty学习篇⑥--ByteBuf源码分析

    什么是ByteBuf? ByteBuf在Netty中充当着非常重要的角色:它是在数据传输中负责装载字节数据的一个容器;其内部结构和数组类似,初始化默认长度为256,默认最大长度为Integer.MAX ...

  10. Java网络编程 -- Netty中的ByteBuf

    由于JDK中提供的ByteBuffer无法动态扩容,并且API使用复杂等原因,Netty中提供了ByteBuf.Bytebuf的API操作更加便捷,可以动态扩容,提供了多种ByteBuf的实现,以及高 ...

随机推荐

  1. 取消putty右键粘贴功能

    还是非常喜欢putty的.就是右键默认的粘贴功能比较讨人厌.

  2. java 基础知识一 初识java

    java  基础知识一初识java 1.java语言的特点 (1)简洁有效(2)可移植性(3)面向对象(4)解释型(5)适合分布式计算 2.java的源文件扩展名为.java 编译后的扩展名为.cla ...

  3. RSA加密算法 C++实现

    上信息安全课,老师布置了几个大作业,其中一个为RSA加密算法的实现,不能用Java写.出于兴趣,决定尝试.完成之后,为了便于查找,于是写下这篇文章,以备后续查看.也供大家一起学习,一起进步. 1.预备 ...

  4. 本地yum服务搭建

    1.准备linux ISO系统镜像文件 (例如:rhel-server-5.5-i386-dvd.iso) 2.linux虚拟机(centos 7  192.168.50.24 ),启动sshd服务 ...

  5. webmagic源码学习(一)

    最近工作主要是一些爬虫相关的东西,由于公司需要构建自己的爬虫框架,在调研过程中参考了许多优秀的开源作品,包括webmagic,webcollector,Spiderman等,通过学习这些优秀的源码获益 ...

  6. 2017-4-26 winform 菜单和工具栏

    如何让radiobutton进行分组: 用Panel    相当于div 菜单和工具栏: MenuStrip(菜单条) ShortcutKeys-------------------------与菜单 ...

  7. hibernate 插入数据时让数据库默认值生效

    用hibernate做数据库插入操作时,在数据库端已经设置了对应列的默认值,但插入的数据一直为null.查找资料发现,原来是hibernate的配置项在作怪. Hibernate允许我们在映射文件里控 ...

  8. office web apps 部署-搭建office web apps服务器

    二.搭建office web apps服务器 相关文件可以去焰尾迭分享的百度网盘下载,下载地址:http://pan.baidu.com/s/1o6tCo8y#path=%252Foffice%252 ...

  9. 【算法系列学习】[kuangbin带你飞]专题十二 基础DP1 C - Monkey and Banana

    https://vjudge.net/contest/68966#problem/C [参考]http://blog.csdn.net/qinmusiyan/article/details/79862 ...

  10. Linux学习之竿头直上

    今天第二讲昨天我们讲解了10基础命令,今早上我继续为大家讲解10个linux常见命令 今天主要讲解与网络相关的命令和软件安装的命名 NetworkManager 与rpm 在windows中我们查看网 ...