netty学习之nio储备

NIO介绍

什么是NIO呢?区别于BIO有什么优点呢?

nio写作new io 或 none blocking io,它是面向缓冲区,结合选择器(多路复用器)的非阻塞io。区别于传统io,更加高效、适用高并发读写环境。

结构组成

那 nio的组件有哪些呢?nio的组件包含buffer、channel、selector.

buffer缓冲区

本质上是一块内存,用来存储数据,缓冲区是数组类型,用于存储不同数据类型的数据。根据使用的场景不同,主要包含有一下几种常见的几种缓冲区
创建的方式及类别:直接缓冲区和非直接缓冲区

  • 直接缓冲区:allocateDirect()创建的是直接缓冲区
  • 非直接缓冲区:allocate()创建的是非直接缓冲区
    ByteBuffer btf=ByteBuffer.allocate(1024);
    ByteBuffer btf1 = ByteBuffer.allocateDirect(1024);

那直接缓冲区和非直接缓冲区有什么区别呢?非直接缓冲区是建立在jvm内存中,实际的读取需要在操作系统和jvm之间进行数据拷贝;直接缓冲区是建立在物理内存中,避免中间拷贝的多余环节。

*优缺点对比:*直接缓冲区避免了拷贝环节,效率更高;缺点是占用资源多,磁盘的写入操作不受控制
*适用场景:*数据本身存活时间长,或者数据量较大的操作建议使用直接缓冲区

直接缓冲区配图
那缓冲区怎么使用呢?
让我们先熟识下他的几个核心属性:position、limit、capacity、mark、clear

  • position:表示缓冲区中正在操作数据的位置
  • limit(界限):表示缓冲区中可操作数据的大小(limit后的数据不能进行操作)
  • capacity:这个是表示缓冲区的最大容量
  • mark:用来标记当前position,可以使用reset()恢复到标记的mark位置
  • clear:清空状态(不是清空数据)

来看看下面这个小栗子:

public void test1() {
    String str="abcde";

    ByteBuffer btf=ByteBuffer.allocate(1024);//创建缓冲区

    System.out.println("---------------allocate()----------------");
    System.out.println(btf.position());//位置
    System.out.println(btf.limit());//界限
    System.out.println(btf.capacity());//容量

    btf.put(str.getBytes());//存放数据
    System.out.println("---------------put()----------------");
    System.out.println(btf.position());//位置
    System.out.println(btf.limit());//界限
    System.out.println(btf.capacity());//容量

    btf.flip();//切换为读模式
    System.out.println("---------------flip()----------------");
    System.out.println(btf.position());//位置
    System.out.println(btf.limit());//界限
    System.out.println(btf.capacity());//容量

    byte[] bts=new byte[btf.limit()];
    btf.get(bts, 0, bts.length);//读取所有字节数
    System.out.println(new String(bts));
    System.out.println("---------------get()----------------");
    System.out.println(btf.position());//位置
    System.out.println(btf.limit());//界限
    System.out.println(btf.capacity());//容量

    btf.clear();//清除所有模式
    System.out.println("---------------clear()----------------");
    System.out.println(btf.position());//位置
    System.out.println(btf.limit());//界限
    System.out.println(btf.capacity());//容量
}

运行后的结果

相信到这里大家应该大致了解什么是缓冲区了

channel信息通道

通道,简而言之就是用于连接点与点,架起信息传输的桥梁。通道根据使用情况的不同,主要有以下几类

  • FileChannel:文件通道,用于文件的读和写
  • DatagramChannel:用于 UDP 连接的接收和发送
  • SocketChannel:把它理解为 TCP 连接通道,简单理解就是 TCP 客户端
  • ServerSocketChannel:TCP 对应的服务端,用于监听某个端口进来的请求
    通道
    下面我们来看看通道的使用方式:
在jdk1.7中NIO.2针对各个通道提供了静态方法open()

SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress(host, port));

ServerSocketChannel ssc = ServerSocketChannel.open();

DatagramChannel dc = DatagramChannel.open( );

FileInputStream fis = new FileInputStream("xx.jpg");
fis.getChannel();
//使用通道完成文件的复制(非直接缓冲区)
    @Test
    public void test1() {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        //获取通道
        FileChannel fisChannel = null;
        FileChannel fosChannel = null;
        try {
            fis = new FileInputStream("1.jpg");
            fos = new FileOutputStream("2.jpg");

            fisChannel = fis.getChannel();
            fosChannel = fos.getChannel();

            ByteBuffer btf = ByteBuffer.allocate(1024);

            //将通道中数据存到缓冲区中
            while (fisChannel.read(btf) != -1) {
                btf.flip();//切换为读模式
                fosChannel.write(btf);
                btf.clear();//清空缓冲区
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fisChannel != null) {
                try {
                    fisChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fosChannel != null) {
                try {
                    fosChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

selector选择器

selector选择器也可称作为多路复用(选择)器。

传统的架构

传统的架构是一个请求对应一个链接,一个链接对应一个线程处理,部分线程的读写操作是无效的,在线程累计的过程中会造成资源的浪费,不适合高并发环境

多路复用选择器使用

多路选择器:服务不会对每个请求都创建线程,请求通过channel通道注册到selector连接器上,当轮询到请求有读写操作的时候才会创建线程,实现请求资源的统一管理,避免资源浪费

选择器的使用:

  • 首先,需要开一一个选择器
    Selector selector = Selector.open();
  • 将通道注册到选择器上
    // 将通道设置为非阻塞模式,因为默认都是阻塞模式的
    channel.configureBlocking(false);
    // 注册
    SelectionKey key = channel.register(selector, SelectionKey.OP_READ);

SelectionKey:针对不同使用场景需要选择不同的事件类型,SelectionKey.OP_READ(可读)、SelectionKey.OP_WRITE(可写)、SelectionKey.OP_ACCEPT(接受tcp请求)、SelectionKey.OP_CONNECT(建立tcp链接)

举个栗子:

public void server() throws IOException {
    //1、获取通道
    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    //2、绑定
    serverSocketChannel.bind(new InetSocketAddress(9898));

    //3.切换为非阻塞模式
    serverSocketChannel.configureBlocking(false);
    //4、创建选择器
    Selector selector = Selector.open();

    //5、注册通道到选择器中
    serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

    //5、查询准备就绪的通道
    while (selector.select() > 0) {
        Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
        while (iterator.hasNext()) {
            SelectionKey next = iterator.next();
            //6、判断是否是接受事件类型
            if (next.isAcceptable()) {
                SocketChannel socketChannel = serverSocketChannel.accept();
                //7、设置为非阻塞
                socketChannel.configureBlocking(false);
                socketChannel.register(selector, SelectionKey.OP_READ);
            } else if (next.isReadable()) {
                SocketChannel sChannel = (SocketChannel) next.channel();
                ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                while (sChannel.read(byteBuffer) > 0) {
                    byteBuffer.flip();
                    System.out.print(new String(byteBuffer.array(), 0, byteBuffer.limit()));
                    byteBuffer.clear();
                }
            }
            //移除类型key
            iterator.remove();
        }
    }

}


 public void client() throws IOException {
        SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
        FileInputStream fis = new FileInputStream("1.jpg");
        fis.getChannel();

        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

        //取消阻塞模式
        socketChannel.configureBlocking(false);

        Scanner sc = new Scanner(System.in);
        while (sc.hasNext()) {
            String str = sc.next();
            byteBuffer.put(((new Date()).toString() + "\n" + str + "\n").getBytes());

            byteBuffer.flip();
            socketChannel.write(byteBuffer);
            byteBuffer.clear();
        }
        socketChannel.close();
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值