Mina Iobuffer中常用方法与介绍

2022-09-05 08:06:17




Limit(int)

如果position>limit, position = limit,如果mark>limit, 重置mark

Mark()

取当前的position的快照标记mark

Reset()

恢复position到先前标记的mark

Clear()

limit=capacity , position=0,重置mark,但是不清空数据,为了从头开始put做准备,其实就是清空数据,因为你put就覆盖了原来的数据

Rewind()

position=0,重置mark,一系列写操作后,为了从头开始get做准备,和clear()有用途上的区别,他大部分是用来从头开始读取,而clear是大部分用来重头开始填充,就是清理的意思

Flip()

limit=position , position=0,重置mask,为了将buf写出做好准备,一般是结束buf操作,将buf写入输出流时调用,这个必须要调用,否则极有可能position!=limit,导致position后面没有数据,每次写入数据到输出流时,必须确保position=limit。

Remaining()

返回limit-position,返回缓冲器中的剩余字节

Wrap(byte[])

组装到新的buffer,capacity=limit=byte[].length,position=0 重置mark

Slice()

分割缓冲器,将remaining的空间形成一个新的buffer,新的position=0,limit=capacity=remaining,重置mark,和主缓冲区内容共享,其它都独立

Duplicate()

缓冲区,内容共享,其它都独立

asReadOnlyBuffer()

和duplicate一样,只是不可写

Compact()

将position和limit之间的字节移到最前面,position=limit-position,这就是这里的压缩的意思,一般是结束buf操作,将buf写入输出流时调用

Position(int)

position=newPosition,如果position<mark,重置mark

Remaining()

返回position和limit之间的字节数

IoBuffer

IoBuffer是MINA内部使用的一个byte buffer,MINA并没有直接使用NIO 的ByteBuffer。不过IoBuffer 是对 ByteBuffer 的一个封装。IoBuffer 中的很多方法都是对 ByteBuffer 的直接继承。只是对 ByteBuffer 添加了一些扩展了更加实用的方法。

基本用法

由于IoBuffer是对Nio的ByteBuffer 的封装,所以基本概念还是相同的,下面简单介绍一下:

1、capacity:该属性描述这个缓冲区最多能缓冲多少个元素,也是Buffer最大存储元素数,这个值是在创建Buffer的时候指定的,且不能修改。

2、Limit:在从Buffer中向Channel中写数据时,limit变量指示了还剩多少数据可以读取,在从Channel中读取数据到Buffer中时,limit变量指示了还剩多少空间可供存放数据。position正常情况下小于或者等于limit。

3、Position:Buffer实际上也就是个array。当你从Channel中读数据时,你把从Channel中读出来的数据放进底层array,position变量用来跟踪截止目前为止已经写了多少数据。更精确的讲,它指示如果下次写Buffer时数据应该进入array的哪个位置。因此如果已经从Channel中读出了3个字节,Buffer的position会被置为3,指向array中第四个位置。

4、Mark:一个可以记忆的Position位置的值,在调用reset()方法时会将缓冲区的Position重置为该索引,并非总是需要定义Mark,但是在定义Mark时,不能将其定义为负数,并且不能让它大于Position,如果定义了Mark,则在该Position或Limit调整为小于该Mark值时,该Mark将被丢弃。

下面通过一个例子来说明:

i、初始状态下:

此时position为0,limit和capacity都被设为9;

image

ii、从Channel中读入4个字节数据到Buffer,这时position指向4(第5个):

image

iii、在做写操作之前,我们必须调用一次flip()方法,这个方法做了两件重要的事情:

  /
     * @return  This buffer
     */
    public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }
1. 将limit设置到当前的position处。
2. 设置position为0。

image

iiii、执行写操作后;

image

iv、执行clear后,position设为0,limit设为capition,mark则丢弃;

  public final Buffer clear() {
        position = 0;
        limit = capacity;
        mark = -1;
        return this;
    }

image

因为IoBuffer是一个抽象类,不能直接实例化,所有使用的时候需要调用allocate方法来进行内存分配;

allocate有两种定义:

   1: // Allocates a new buffer with a specific size, defining its type (direct or heap)
   2: public static IoBuffer allocate(int capacity, boolean direct)
   3:
   4: // Allocates a new buffer with a specific size
   5: public static IoBuffer allocate(int capacity)

这里:

capacity:buffer的大小;

direct:如果为true,则得到direct buffer,如果为false,则得到heap buffer

direct buffer和heap buffer的区别分析:

Direct Buffer不是分配在堆上的,它不被GC直接管理(但Direct Buffer的JAVA对象是归GC管理的,只要GC回收了它的JAVA对象,操作系统才会释放Direct Buffer所申请的空间),它似乎给人感觉是“内核缓冲区(buffer in kernel)”。Heap Buffer则是分配在堆上的,或者我们可以简单理解为Heap Buffer就是byte[]数组的一种封装形式。当我们把一个Heap Buffer写入Channel的时候,实际上底层实现会先构建一个临时的Direct Buffer,然后把Heap Buffer的内容复制到这个临时的Direct Buffer上,再把这个Direct Buffer写出去。因此把一个Direct Buffer写入一个Channel的速度要比把一个Heap Buffer写入一个Channel的速度要快。但是Direct Buffer创建和销毁的代价很高,所以要用在尽可能重用的地方。

public static IoBuffer allocate(int capacity)的用法:
   1: // 设置Allocates分配的默认类型,这里设为heap buffer.
   2:  IoBuffer.setUseDirectBuffer(false);
   3:  // 返回一个新的heap buffer.
   4:  IoBuffer buf = IoBuffer.allocate(1024);

IoBuffer允许生成一个自动扩展的buffer(这也是没有选择使用NIO的ByteBuffer的原因之一);通过设置AutoExpand属性即可:

   1: IoBuffer buffer = IoBuffer.allocate(8);
   2: buffer.setAutoExpand(true);
   3:
   4: buffer.putString("12345678", encoder);
   5:
   6: // Add more to this buffer
   7: buffer.put((byte)10);
善于将复杂问题简单化




  1.      String str ="helloWorld";
  2.         ByteBuffer buff  = ByteBuffer.wrap(str.getBytes());
  3.         System.out.println("position:"+buff.position()+"\t limit:"+buff.limt());
  4. //读取两个字节
  5.         buff.get();
  6.         buff.get();
  7.         System.out.println("position:"+ buff.get(buff.position())+"\t limit:"+buff.limit());
  8.         buff.mark();
  9.         System.out.println("position:"+buff.position()+"\t limit:"+buff.limit());
  10.         buff.flip();
  11.         System.out.println("position:"+buff.position()+"\t limit:"+buff.limit());

Java代码收藏代码
  1. 输出结果:
  2. position:0  limit:10
  3. position:2  limit:10
  4. position:2  limit:10
  5. position:0  limit:2
 我们以每位开发人员熟悉的”helloworld“,用ByteBuffer将字符串包装,由于ByteBuffer是一个抽象类,通过wrap包装的对象将实际返回的是一个HeapByteBuffer对象。由此可知HeapByteBuffer是ByteBuffer的子类,同样的ByteBuffer又是Buffer抽象类的子类。以上提到的mark、position、limit、flip、reset都是出自于Buffer这个抽象类。
下面我们来解析几个方法的,当我们调用了wrap方法后Buffer中初始化的结构是:
注释:
m:mark;
p:position;
L:limit;
   初始情况下mark是指向第一个元素之前的的即-1,postion为指向第一个元素为0.而Limit是被赋值为byte[]的长度。
因此这就是打印结果的第一行。
mpL
-10123456789
HELLOWORLD
   当我们连续调用两次get()方法获得两个个字节,每次调用都会触发position++操作,那么此时position就会移动到index = 2的的地方,而这个时候Limit和mark是不会发生变化的。如果将读取的两个字节打印会是H和E,因此执行结果第二行会有position:2  limit:10结果.
m、pL
-10123456789
HELLOWORLD
     读取完毕后我们使用mark,这个时候mark会从-1移动到2和position指向同一个元素,可以看见Limit是不会发生改变的。
mpL
-10123456789
HELLOWORLD
    使用了mark标记的当前的position后,如果们调用flip,意思是做好读取的准备,这个时候Limit就会指向position的位置,并将mark和position还原为初始值。limit当前的就为2,就是说当前可以读的字节数是2。
我们可以尝试一下如下代码:
Java代码收藏代码
  1. System.out.println((char)buff.get()+""+(char)buff.get());
    输出结果:he
    貌似这也没什么稀奇的,如果你在代码换成
Java代码收藏代码
  1. // System.out.println((char)buff.get()+""+(char)buff.get()
  2.    System.out.println((char)buff.get()+""+(char)buff.get()+""+(char)buff.get());
Java代码收藏代码
  1. 输出结果:<preclass="java" name="code">position:0    limit:10
  2. Exception in thread"main" java.nio.BufferUnderflowException
  3.     at java.nio.Buffer.nextGetIndex(Buffer.java:474)
  4.     at java.nio.HeapByteBuffer.get(HeapByteBuffer.java:117)
  5.     at com.taobao.moxing.notify.Main.main(Main.java:33)position:2    limit:10
  6. position:2   limit:10
  7. position:0   limit:2
  8. </pre>
   为什么会抛异常呢?原因是limit的含义就像一个窗口,你当前能读到的数据就是当前窗口限制的(本例中即为2),如果这个窗口之外的所有元素都是不可读的。至此我想你和我就应该明白这几个参数的含义了吧。
    而至于reset方法,它是将当前的position设置为0,
     rewind是将mark重置为-1,position重置为0;
    clear方法是真正的重置,将mark=-1,position=0,limit=capacity(即当前buffer的容量)
  public final Buffer rewind() {
        position = 0;
        mark = -1;
        return this;
    }
  public final Buffer clear() {
        position = 0;
        limit = capacity;
        mark = -1;
        return this;
    }




本文例子来源于网络

  • 作者:zs319428
  • 原文链接:https://blog.csdn.net/zs319428/article/details/70231678
    更新时间:2022-09-05 08:06:17