【稀里糊涂学Netty】Nio Buffer

本篇文章介绍一下java中的Nio中的Buffer,注意不是Netty中的,后面会讲解Netty中的Buffer

什么是Buffer

Buffer本质就是一个可以读写的内存块,它提供了一些方法,可以轻松的往里面写数据和读数据。
Nio中Channel提供了从文件或者网络读取数据的渠道,但是读取和写入数据时,必须经由Buffer

Nio中Buffer有以下几种:

  • IntBuffer
  • FloatBuffer
  • CharBuffer
  • DoubleBuffer
  • ShortBuffer
  • LongBuffer
  • ByteBuffer

可以看出八种基本数据类型除了boolean,都有一个对应的Buffer

下面以IntBuffer为例讲解一下Buffer中的方法,然后通过实例展示

  • public static IntBuffer allocate(int capacity)
    创建一个IntBuffer对象。指定这个IntBuffer的容量
    //可以放5个数字到这个buffer中
    IntBuffer intBuffer = IntBuffer.allocate(5);
  • public abstract IntBuffer put(int i);
    向IntBuffer中放一个整数
    //创建一个存放5个整数的buffer
    IntBuffer intBuffer = IntBuffer.allocate(5);
    //将整数1放到buffer中
    intBuffer.put(1);
  • public final Buffer flip()
    切换buffer的读写。内部源码实际的逻辑就是通过控制position的值来进行操作的
    
  • public final boolean hasRemaining()
    判断buffer中是否有数据,一般在while循环中当作条件。

例子:

public class BufferTest {
  public static void main(String[] args) throws Exception {

    IntBuffer intBuffer = IntBuffer.allocate(5);
    //intBuffer.put(1);

    for (int i = 0; i < intBuffer.capacity(); i++) {
      intBuffer.put(i);
    }

    // 如何从buffer读数据,通过这个方法将buffer切换为模式,然后就可以读数据,
    // 如果想往buffer里再写数据,就在执行一次这个方法转换一下就行
    intBuffer.flip();

    // 如果intBuffer中还有数据
    while (intBuffer.hasRemaining()) {
      System.out.println(intBuffer.get());
    }
  }
}

执行结果为:


下面通过源码,来看一下

  1. buffer中的数据从在那?

    在上面的实例中,我们创建了一个int的buffer,可以保存4个数据,那么这4个数据其实就是保存在这个红框的hb数组中的。(8个buffer都会有一个对应的hb)。
  2. buffer中的4个属性的意思

    属性描述
    capacitybuffer可以存放的最大数据,创建buffer是确定,然后就不可以在变了
    limit表示缓冲区的当前终点。就是设置以后,读写buffer时不能超过这个limit的位置
    position表示将要读取数据的位置或者写入数据的位置
    mark标记


    position为0表示要将数据写入到的位置是下标为0的位置,下一次就会变为1以此类推。但是因为limit为5,则表示当position为4时就不能再添加了。这就是limit的作用。如果现在吧limit设置为1,那么添加完第一数据后,就不能再添加了,即便是我们开始设置的容量capacity为5,也不能再添加数据了,这就是limit的作用。

常用的api解释

 

Nio还支持通过多个Buffer(即Buffer数组)完成读写操作,也就是Scattering 和Gathering
什么是Scattering?
将数据写入buffer时,可以依次写入,比如一个buffer写满了,就往buffer数组的下一个buffer里写
什么是Gathering
从buffer读数据时,可以采用buffer数组,比如一个buffer读完之后,就从buffer数组的下一个buffer中继续读。

下面通过例子看一下:
 

public static void main(String[] args) throws Exception {

    ServerSocketChannel serverSocketChannel =ServerSocketChannel.open();
    InetSocketAddress inetSocketAddress = new InetSocketAddress(7000);
    serverSocketChannel.socket().bind(inetSocketAddress);

    ByteBuffer[] byteBuffers = new ByteBuffer[2];
    byteBuffers[0] = ByteBuffer.allocate(5);
    byteBuffers[1] = ByteBuffer.allocate(3);

    SocketChannel socketChannel = serverSocketChannel.accept();
    int messageLength = 8; //假定从客户端接收8个字节
    while (true) {
      int byteRead = 0;
      while (byteRead < messageLength) {
        long l = socketChannel.read(byteBuffers);// 读取的字节数
        byteRead += l; // 累计读取的字节数
        System.out.println("byteRead=" + byteRead);
        // 使用流打印,看看当前buffer的position和limit
        Arrays.asList(byteBuffers).stream().map(buffer->"position:" + buffer.position() + "limit:" + buffer.limit()).forEach(System.out::println);
      }
      //对所有的buffer进行flip
      Arrays.asList(byteBuffers).forEach(buffer->buffer.flip());
      //将数据读出,显示到客户端
      long byteWrite = 0;
      while(byteWrite < messageLength) {
        long l = socketChannel.write(byteBuffers);
        byteWrite += l;
      }

      //将所有的buffer进行clean
      Arrays.asList(byteBuffers).forEach(buffer->buffer.clear());
      System.out.println("byteRead=" + byteRead + "byteWrites=" + byteWrite + "messageLength=" + messageLength);

    }
}

运行启动程序后,然后通过cmd启动telenet,方式如下:
1.win + R,然后输入cmd
2.打开cmd后,输入telnet 127.0.0.1 7000 连接我们的socket
3.连接成功后,输入helloa六个字符

结果:然后可以发现helloa在填充完第一个数组后,继续想第二个数组中填写。然后从字节数组中读的时候,也是把两个数组都读出来了。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值