一、前言

  前面已经学习了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. matlab 2016a破解中文版安装教程

    之前电脑重装过,所以要重新安装一个matlab,在大三的时候学过matlab,信息老师给的安装包,但是不知道放哪里去了,记忆力不好,找了些网上的教程和下载地址,真的是坑,一些都是不行的,在这里记录下m ...

  2. Android -- 贝塞尔二阶实现饿了么加入购物车效果

    1,上周我们实现了简单的三阶贝塞尔曲线效果实例,今天是使用二阶贝塞尔曲线加动画实现的加入购物车效果,在码代码过程中出现了些问题,过一下和大家来探讨探讨,先看一下效果图 2,从上面的效果来看我们基本上可 ...

  3. preg_*匹配的字符串长度限制问题以及nginx,php上传文件过大问题

    问题背景 使用插件上传高清图片,用的插件base64转码的,上传失败,接口提示:413 (Request Entity Too Large) 问题分析与解决  首先想到的是nginx和php的服务器配 ...

  4. Spring+SpringMVC+MyBatis+easyUI整合优化篇(八)代码优化整理小记及个人吐槽

    日常啰嗦 这两天也一直在纠结这一篇文章该写什么东西,前面临时加的两篇文章就有些打乱了整体节奏,这一篇又想去写一下代码层面优化的事情,可是也不太能抓住重要的点,不太确定从何入手,因为这件事情牵涉了太多技 ...

  5. 【模板】二分图最大权完美匹配KM算法

    hdu2255模板题 KM是什么意思,详见百度百科. 总之知道它可以求二分图最大权完美匹配就可以了,时间复杂度为O(n^3). 给张图. 二分图有了边权,求最大匹配下的最大权值. 所以该怎么做呢?对啊 ...

  6. jQuery扩展函数设置所有对象只读

    jQuery(function ($) {             $.fn.disable = function () {                 return this.each(func ...

  7. angular ng-bind

    <body ng-app=""> <div ng-controller="firstController"> <input typ ...

  8. 生肖年(switch练习)

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  9. SQL生成一年每一天的时间列表的几种方法

    工作好几年了,一直没有写博客,准备捡起来...   以下脚本适用环境:SQL SERVER (starting with 2012)   1.构建序列:   /*1-1:利用交叉连接,推荐下列这种写法 ...

  10. tomcat的环境搭建

    tomcat搭建过程还是比较简单的,只需要安装好jdk,然后配置好环境变量,最后把tomcat安装上开启就可以了. 首先下载jdk,然后把下载下来的jdk放到/usr/local下,然后用rpm -i ...