zoukankan      html  css  js  c++  java
  • ByteBuf 使用

    netty版本

    netty版本:io.netty:netty-all:4.1.33.Final

    简介

    1. 网络数据的基本单位总是字节,java NIO提供ByteBuffer作为字节的容器,但是ByteBuffer使用起来过于复杂和繁琐。
    2. ByteBuf是netty的Server与Client之间通信的数据传输载体(Netty的数据容器),它提供了一个byte数组(byte[])的抽象视图,既解决了JDK API的局限性,又为网络应用程序的开发者提供了更好的API
    3. ByteBuffer缺点
      • ByteBuffer长度固定,一旦分配完成,它的容量不能动态扩展和收缩,当需要编码的POJO对象大于ByteBuffer的容量时,会发生索引越界异常;
      • ByteBuffer只有一个标识位置的指针position,读写的时候需要手工调用flip()rewind()等,使用者必须小心谨慎地处理这些API,否则很容易导致程序处理失败;
      • ByteBuffer的API功能有限,一些高级和实用的特性它不支持,需要使用者自己编程实现。
    4. ByteBuf优点
      • 容量可以按需增长
      • 读写模式切换不需要调用flip()
      • 读写使用了不同的索引
      • 支持方法的链式调用
      • 支持引用计数
      • 支持池化
      • 可以被用户自定义的缓冲区类型扩展
      • 通过内置的复合缓冲区类型实现透明的零拷贝

    API详解

    1. ByteBuf工作机制:ByteBuf维护了两个不同的索引,一个用于读取,一个用于写入。readerIndexwriterIndex的初始值都是0,当从ByteBuf中读取数据时,它的readerIndex将会被递增(它不会超过writerIndex),当向ByteBuf写入数据时,它的writerIndex会递增。
    2. 名称以readXXX或者writeXXX开头的ByteBuf方法,会推进对应的索引,而以setXXXgetXXX开头的操作不会。
    3. 在读取之后,0~readerIndex的就被视为discard的,调用discardReadBytes方法,可以释放这部分空间,它的作用类似ByteBuffercompact()方法。
    4. readerIndexwriterIndex之间的数据是可读取的,等价于ByteBufferpositionlimit之间的数据。writerIndexcapacity之间的空间是可写的,等价于ByteBufferlimitcapacity之间的可用空间。

    索引变化图

    1. 初始分配

          
    2. 写入N个字节

         
    3. 读取M(<N)个字节之后

          
    4. 调用discardReadBytes操作之后

         
    5. 调用clear操作之后

          

    创建

    1. 推荐通过一个Unpooled的工具类来创建新的buffer而不是通过构造器来创建

    2. 创建堆缓冲区

    @Test
    public void testHeapByteBuf() {
        ByteBuf heapBuf = Unpooled.buffer(10);
        if (heapBuf.hasArray()) {
            byte[] array = heapBuf.array();
            int offset = heapBuf.arrayOffset() + heapBuf.readerIndex();
            int length = heapBuf.readableBytes();
            //0,0        
            logger.info("offset:{},length:{}", offset, length);
        }
    }

    3. 直接内存缓冲区

    @Test
    public void testDirectByteBuf() {
        ByteBuf directBuffer = Unpooled.directBuffer(10);
        if (!directBuffer.hasArray()) {
            int length = directBuffer.readableBytes();
            byte[] array = new byte[length];
            ByteBuf bytes = directBuffer.getBytes(directBuffer.readerIndex(), array);
            //0,0
            logger.info("offset:{},length:{}",bytes.readerIndex() , array.length);
        }
    }

    访问

    1. ByteBuf提供了两个指针变量来支持顺序读写操作readerIndex用来支持读操作, writerIndex用来支持写操作。下图展示了ByteBuf是如何被两个索引分成三个区域的

    2. 可读字节:ByteBuf的可读字节分段存储了实际数据。新分配的、包装的或者复制的缓冲区的默认的readerIndex值为0。任何名称以read或者skip开头的操作都将检索或者跳过位于当前readerIndex的数据,并且将它增加已读字节数。如果被调用的方法需要一个ByteBuf参数作为写入的目标,并且没有指定目标索引参数,那么该目标缓冲区的writeIndex也将增加(例如:readBytes(ByteBuf dst))

    //读取所有可读的字节
    ByteBuf buffer = ...;
    while (buffer.readable()) {
       System.out.println(buffer.readByte());
    }

    3. 可写字节:可写字节分段是指一个拥有未定义内容的、写人就绪的内存区域。 新分配的缓冲区的writerindex的默认值为0。 任何名称以write开头的操作都将从当前的 writerIndex处开始写数据,并将它增加已经写入的字节数。如果写操作的目标也是 ByteBuf时,并且没有指定源索引的值,则源缓冲区的readerIndex也同样会被增加相同的大小(例如:writeBytes(ByteBuf dest))。

    @Test
    public void testWrite() {
        ByteBuf heapBuf = Unpooled.buffer(10);
        while (heapBuf.writableBytes() > 4) {
            heapBuf.writeInt(new Random().nextInt());
        }
    }

    4. 丢弃字节:可丢弃字节的分段包含了已经被读过的字节。通过调用discardRead­Bytes()方法,可以丢弃它们并回收空间。这个分段的初始大小为0,存储在readerIndex中, 会随着read操作的执行而增加 (get*操作不会移动readerindex )

    5. 虽然你可能会倾向于频繁地调用discardReadBytes()方法以确保可写分段的最大化,但是请注意,这将极有可能会导致内存复制, 因为可读字节必须被移动到缓冲区的开始位置。我们建议只在有真正需要的时候才这样做,例如,当内存非常宝贵的时候

    6. 清除buffer索引:你可以通过调用clear()readerIndexwriterIndex都设为0。这不会清除buffer内容(例如用0填充), 他仅仅是清除了两个指针。请注意这个操作的语义和ByteBuffer.clear()是不一样的。调用clear()比调用discardReadBytes()轻量的多,因为只是重置索引而不会复制内存

    7. 查找操作:

    @Test
    public void testFind() {
        ByteBuf heapBuf = Unpooled.buffer(13);
        heapBuf.writeByte(new Random().nextInt());
        heapBuf.writeByte(new Random().nextInt());
        heapBuf.writeBytes("
    ".getBytes());
        int i = heapBuf.indexOf(0, 12, (byte) '
    ');
        //2
        System.out.println(i);
        i = heapBuf.forEachByte(ByteProcessor.FIND_CRLF);
        //2
        System.out.println(i);
    }

    8. 派生缓冲区为ByteBuf提供以专门的方式呈现其内容的视图。

    duplicate()
    slice()
    slice(int, int)
    Unpooled.unmodifiableBuffer ()
    order (ByteOrder)
    readSlice (int)
    
    每个这些方法都将返回一个新的ByteBuf实例,它具有自己的读索引、写索引和标记
    索引。其内部存储和 JDK 的ByteBuffer一样也是共享的。这使得派生缓冲区的创建成本
    是很低廉的,但是这也意味着,如果你修改了它的内容,也同时修改了其对应的源实例,所以要小心。

     

  • 相关阅读:
    Person Re-identification 系列论文笔记(四):Re-ID done right: towards good practices for person re-identification
    [转]embedding technic:从SNE到t-SNE再到LargeVis
    Person Re-identification 系列论文笔记(三):Improving Person Re-identification by Attribute and Identity Learning
    Person Re-identification 系列论文笔记(二):A Discriminatively Learned CNN Embedding for Person Re-identification
    Person Re-identification 系列论文笔记(一):Scalable Person Re-identification: A Benchmark
    对The Curse of Dimensionality(维度灾难)的理解
    [转]The Curse of Dimensionality(维数灾难)
    [MS Office Word] 小型大写字母
    [MS Office Word] 英文字母大小写快捷键
    [MATLAB] asv文件
  • 原文地址:https://www.cnblogs.com/ylz8401/p/14327201.html
Copyright © 2011-2022 走看看