Netty学习(二)介绍nio的核心组件buffer和channel

125 阅读2分钟

一、NIO是个啥?

  1. 同步非阻塞
  2. 有三个核心组件:
    • Channel:通道
    • Buffer:缓冲区
    • Selector:选择器
  3. 非阻塞,我理解是啥?
  4. NIO对于缓冲区的读写是双向的,这个是有别于BIO的;同时,NIO在操作或者写操作后,需要使用flip方法进行切换。
package com.angliali.nio;

import java.nio.IntBuffer;

public class BasicBuffer {
    public static void main(String[] args) {
        //初始化一个大小为5的缓冲区
        IntBuffer intBuffer = IntBuffer.allocate(5);

        //向缓冲区中防止元素
        for (int i = 0 ;i<5;i++ ){
            intBuffer.put(i);
        }

        //从缓冲器中读取元素
        intBuffer.flip();
        while (intBuffer.hasRemaining()){
            System.out.println(intBuffer.get());
        }

    }
}
  1. NIO的模型通过画图进行说明。

二、缓冲区Buffer

  • 本质上是一个读写的内存块
  • 从源码的角度分析
看下缓冲区的父类
public abstract class Buffer {
    // Invariants: mark <= position <= limit <= capacity
    private int mark = -1;//标识符
    private int position = 0;//表示下一次读写的位置下表
    private int limit;//表示缓冲区当前的重点,不能去操作超过重点的位置
    private int capacity;//容量,表示缓存区的大小
。。。
}

在子类中,元素都是存储在一个数组中。
public abstract class IntBuffer
    extends Buffer
    implements Comparable<IntBuffer>
{
    final int[] hb;                  // Non-null only for heap buffers,实际用来存储元素的数组
    final int offset;
    boolean isReadOnly;                 // Valid only for heap buffers
    }

三、通道Channel

  • 通道类似流,可以对通道进行读写操作,上面的图也能看出来
  • Channel是个接口,源码
public interface Channel extends Closeable {
    public boolean isOpen();
    public void close() throws IOException;

}
  • 常用的实现类有:FileChannel、ServerSocketChannel、SocketChannel和DatagramChannel由于udp数据的读写
  • 特别介绍下FileChannel
public abstract class FileChannel
    extends AbstractInterruptibleChannel
    implements SeekableByteChannel, GatheringByteChannel, ScatteringByteChannel
{
//read方法是将通读的数据存放到缓冲区
public abstract int read(ByteBuffer dst) throws IOException;
//write方法是将缓冲区的数据写到通道
public abstract int write(ByteBuffer src) throws IOException;
}

下面我写了一下例子:

例子1:使用nio把数据写到文件里面

package com.angliali.nio;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class NioFileChannel {
    public static void main(String[] args) throws IOException {
        String str = "hello";
        //使用nio把字符串写入到文件:H:\book\md\NIOChannel.txt

        //实现缓冲区
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        byteBuffer.put(str.getBytes());
        byteBuffer.flip();

        //实现通道
        FileOutputStream fileOutputStream = new FileOutputStream("H:\\book\\md\\NIOChannel.txt");
        FileChannel fileChannel = fileOutputStream.getChannel();

        //完成缓冲区写入到通道
        fileChannel.write(byteBuffer);
        //关闭流
        fileOutputStream.close();
    }
}

例子2:使用nio将文件内容输出到控制台

package com.angliali.nio;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/*
    使用nio,将文件:H:\book\md\NIOChannel.txt 内容读出到控制台
 */
public class NioFileChannelRead {
    public static void main(String[] args) throws IOException {
        //创建文件的输入流
        FileInputStream fileInputStream = new FileInputStream("H:\\book\\md\\NIOChannel.txt");
        //获得通道
        FileChannel fileChannel = fileInputStream.getChannel();
        //获得缓冲区
        ByteBuffer byteBuffer = ByteBuffer.allocate(10);
        //将通道中的数据写入到缓冲区
        fileChannel.read(byteBuffer);

        byteBuffer.flip();
        System.out.println(new String(byteBuffer.array()));

        fileInputStream.close();

    }
}

例子3:使用一个缓冲区完成两个文件内容的拷贝

package com.angliali.nio;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/*
    使用一个缓冲完成文件1到文件2的内容的拷贝
 */
public class NioFileChannelFileCopy {

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

        //输入流
        FileInputStream fileInputStream = new FileInputStream("H:\\book\\md\\NIOChannel.txt");
        //输出流
        FileOutputStream fileOutputStream = new FileOutputStream("H:\\book\\md\\1.txt");

        //获得通道
        FileChannel channelRead = fileInputStream.getChannel();
        FileChannel channelWrite = fileOutputStream.getChannel();

        //缓冲区
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

        //将文件1的通道内容写入到缓冲区
        while(true){
            byteBuffer.clear();
            int length = channelRead.read(byteBuffer);
            if(length == -1){
                break;
            }
            byteBuffer.flip();
            //将缓冲区的内容写入到通道2
            channelWrite.write(byteBuffer);
        }
    }
}
  • 还有一个重要的概念就是分散和聚集,理解起来就是,我们可以使用一个缓冲区的数组去处理请求,在使用时,会去一次填满缓冲区