Netty系列-一分钟了解ByteBuffer和ByteBuf结构

        上一篇文章BIO、NIO扫盲主要介绍了BIO和NIO模型网络结构,并通过简单代码说明BIO和NIO核心功能及使用。NIO通过多路复用选择器Selector解耦server和client的网络连接,只有当client对应的channel准备好相关事件后,server端才会作出相应回馈,通过这种机制支持网络高并发。但使用NIO类库和API繁杂,要考虑线程安全、失败缓存、网络闭包等问题,工作量和难度都很大。Netty是业界最流行的NIO框架之一,经历大规模商业应用考验,成熟、稳定、性能高。接下来介绍下NIO网络通信中java nio ByteBuffer和Netty中ByteBuf中主要方法属性。

     ByteBuffer

        Java NIO提供ByteBuffer作为字节容器,但只有一个位置指针用于处理读写操作,每次读写时候都需要额外调用flip()和clear()方法,否则功能将会出错。查看ByteBuffer源码如下:

图-ByteBuffer源码

     mark:调用mark()方法的话,mark值将存储当前position的值,等下次调用reset()方法时,会设定position的值为之前的标记值;

     position:是下一个要被读写的元素的数组下标索引,该值会随get()和put()的调用自动更新;

     limit:是缓冲区中第一个不能读写的元素的数组下标索引,也可以认为是缓冲区中实际元素的数量;

     capacity:是缓冲区能够容纳元素的最大数量,这个值在缓冲区创建时被设定,而且不能够改变,如下,创建了一个最大容量为10的字节缓冲区;

      四者关系如下:

       0 <=mark <= position <= limit <= capacity

    具体功能展示:

    创建ButterBuffer:

      ByteBuffer bf = ByteBuffer.allocate(10);

      创建初始化缓冲区,可以看到初始值position=0,而limit=capacity=初始化长度

图-创建ByteBuffer

     往ByteBuffer灌入数据

        bf.put((byte)'H').put((byte)'e').put((byte)'l').put((byte)'l').put((byte)'0')

        向缓冲区存入数据时,没存储一个字符,position索引后移一位


图-向缓冲区输入hello

      调用flit刷新缓冲区为读模式

        bf.flip()

        缓冲区有两种模式,分别是写模式和读模式,这两种模式通过使用flip方法进行模式。如上将缓冲区切换为读模式,则position变成了初值位置0,而limit变成了写模式下position位置。

图-使用flit切换缓冲区为read模式

      读取缓冲区中到内容:get()

        System.out.println((char)bf.get()+""+(char)bf.get());

图-读取缓冲区内容

        调用mark(),标记position位置  

            bf.mark()

图-调用mark标记position位置

             标记完position后,继续读取缓冲区区内容

            System.out.print((char)bf.get()+""+(char)bf.get())

图-继续读取缓冲区数据

               使用reset()方法可以使position回到mark存储到位置
                bf.reset()

图-使用reset方法,将position位置调整mark记录到位置
图-position位置调整

     调用compact()方法,清空已读取数据空间,进行数据到重新填充

       bf.compact()

        compact方法当缓冲区数据写入channel时没有一次性写入完成,通过执行compact方法,将没有写完的数据重新移动到缓冲区到初始位置,position调整为没有写完数据的下个索引,limit调整为capacity。这样buffer可以循环使用,继续向buffer写入数据了。

图-使用compact压缩缓冲区

    使用clear清空缓冲区

        bf.clear()

图-clear初始化缓冲区

    ByteBuf

       ByteBuf是Netty框架封装的数据缓冲区,区别于ByteBuffer需要有position、limit、flip等属性和操作来控制byteBuffer数据读写,Bytebuf通过两个位置指针来协助缓冲区的读写操作,分别是readIndex和writerIndex。

        初始化ByteBuf时,readIndex和writerIndex取值一开始是0,随着数据的写入writerIndex会增加,读取数据会使readIndex增加,但不会超过writerIndex。在读取之后0-readIndex被视为discard,调用discardReadBytes方法,释放这部分空间,作用类似于ByteBuffer的compact方法,移除无用数据,实现缓冲区的重复使用。

     初始化ByteBuf

        ByteBuf bf= Unpooled.buffer(10,100)     

图-分配ByteBuf

       write:写入N个字节之后ByteBuf

图-向ByteBuf写入N个字节

          read:读取M个字节后(M

图-读取M个字节后

        discardReadBytes:

图-使用discardReadBytes进行数据压缩

      总结

        在上面的介绍中可以看到Netty中的ByteBuf使用更加方便,还提供了查找操作(indexOf(int fromindex,int toIndex,byte value)、bytesBefore(Byte value))、数据复制(Derived buffers)、ByteBuffer和ByteBuf互相转化等功能更强大的方法。

你可能感兴趣的:(Netty系列-一分钟了解ByteBuffer和ByteBuf结构)