美文网首页
NIO核心组件 channel、buffer、selector

NIO核心组件 channel、buffer、selector

作者: 南园故剑00 | 来源:发表于2020-06-06 23:07 被阅读0次

1. 三个核心组件 channel、buffer、selector


  1. 每个channel都会对应一个buffer
  2. selector对应一下线程,一个线程对应多个channel
  3. 程序切换到哪个channel是由事件event决定的
    4.selector会根据不同的事件在各个通道上切换
  4. buffer是一个内存块。底层是有一个数组的
  5. 数据的读取写入是通过buffer。BIO要么是输入流或者是输出流,不能双向,但是NIO是可以读也可以写的,需要flip方法切换。
  6. channel是双向的,可以反映底层操作系统的情况。比如linux,底层的操作系统通道是双向的。

2. 面向缓冲区、或者面向块编程的

数据读取到一个它稍后处理的缓冲区,需要时可以在缓冲区中前后移动,增加了处理过程的灵活性,使用它可以提供非阻塞的高伸缩性网络。

3. 非阻塞

一个线程从某通道发送请求或者读取数据,但是它仅得到目前可用的数据。如果目前没有数据可用时,就什么都不会获取,而彼不是保持线程阻塞,所以直至数据变得可以读取之前,该线程可以继续做其他的事情。非阻塞写也是如如,一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以做其他的事情

4. BIO和NIO的比较

  1. BIO以流的方式处理数据,而NIO以块的方式处理数据,块IO的效率比流IO高很多
  2. BIO通过字节流和字符流进行操作,而NIO基于channel和buffer进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中
  3. selector用于监听多个通道的事件(连接请求、数据到达等)。因为使用单个线程就可以监听多个客户端通道

5. HTTP2.0使用了多路复用技术,做到了同一个连接并发处理多个请求,而且并发数量比HTTP1.1大了好几个数量级。

6. Buffer

  1. Buffer可以理解成数组,它通过以下3个值描述状态:

    a) position:下一个元素的位置;

    b) limit:可读取或写入的元素总数,position总是小于或者等于limit;

    c) capacity:Buffer最大容量,limit总是小于或者等于capacity






package com.sgg.buffer;

import java.nio.IntBuffer;

/**
 * @description: Buffer 基本使用
 * @date : 2020/6/6 19:25
 * @author: zwz
 */
public class BasicBuffer {
    public static void main(String[] args) {
        //举例说明一个buffer的使用
        //创建一个buffer,大小为5,即可以存放5个int
        IntBuffer intBuffer = IntBuffer.allocate(5);
//        intBuffer.put(10);
//        intBuffer.put(11);

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

        //从buffer读取数据
        //将buffer读写切换
        intBuffer.flip();
        while (intBuffer.hasRemaining()) {
            System.out.println(intBuffer.get());
        }
    }
}
package com.sgg.buffer;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

/**
 * @description: MappedByteBuffer
 * 1. 可以让文件直接在内存(堆外内存)中修改,操作系统不需要拷贝一次
 * 堆外内存  https://blog.csdn.net/ZYC88888/article/details/80228531
 * @date : 2020/6/6 21:33
 * @author: zwz
 */
public class MappedByteBufferTest {
    public static void main(String[] args) throws IOException {

        /*
         * RandomAccessFile
         * 三、对该工具类的价值分析
                1、大型文本日志类文件的快速定位获取数据:

                得益于seek的巧妙设计,我认为我们可以从超大的文本中快速定位我们的游标,例如每次存日志的时候,我们可以建立一个索引缓存,索引是日志的起始日期,value是文本的poiniter 也就是光标,这样我们可以快速定位某一个时间段的文本内容

                2、并发读写

                emmm也是得益于seek的设计,我认为多线程可以轮流操作seek控制光标的位置,从未达到不同线程的并发写操作。

                3、更方便的获取二进制文件

                通过自带的读写转码(readDouble、writeLong等),我认为可以快速的完成字节码到字符的转换功能,对使用者来说比较友好。
                *
                原文链接:https://blog.csdn.net/qq_31615049/article/details/88562892
         *
         */
        RandomAccessFile randomAccessFile = new RandomAccessFile("E:\\CODE\\Architecture\\netty\\netty-all\\file01.txt", "rw");
        FileChannel channel = randomAccessFile.getChannel();
        /*
         * 1. 使用读写模式
         * 2. 可以直接修改的起始位置
         * 3. 是映射内存的大小,即将file01.txt的多少个字节映射到内存.
         */
        MappedByteBuffer map = channel.map(FileChannel.MapMode.READ_WRITE, 0, 5);
        map.put(0, (byte) 'H');
        //put 超过5 下标越界
        map.put(3, (byte) '9');
        randomAccessFile.close();
    }
}
package com.sgg.buffer;

import java.nio.ByteBuffer;

/**
 * @description: 按相应的类型获取
 * @date : 2020/6/6 21:19
 * @author: zwz
 */
public class NIOByteBufferPutGet {

    public static void main(String[] args) {
        ByteBuffer buffer = ByteBuffer.allocate(64);
        buffer.putInt(100000000);
        buffer.putLong(9);
        buffer.putChar('尚');
        buffer.putShort((short) 4);
        buffer.flip();
        System.out.println();
        System.out.println(buffer.getShort());
        System.out.println(buffer.getInt());
        System.out.println(buffer.getLong());
        System.out.println(buffer.getChar());

    }
}
package com.sgg.buffer;

import java.nio.ByteBuffer;

/**
 * @description: 只读
 * @date : 2020/6/6 21:19
 * @author: zwz
 */
public class NIOByteBufferReadOnly {

    public static void main(String[] args) {
        ByteBuffer buffer = ByteBuffer.allocate(64);
        for (int i = 0; i < 64; i++) {
            buffer.put((byte) i);
        }
        buffer.flip();
        //得到一个只读的buffer
        ByteBuffer readOnlyBuffer = buffer.asReadOnlyBuffer();
        System.out.println(readOnlyBuffer.getClass());  // class java.nio.HeapByteBufferR

        while (readOnlyBuffer.hasRemaining()) {
            System.out.println(readOnlyBuffer.get());
        }

        readOnlyBuffer.put((byte) 5);  //java.nio.ReadOnlyBufferException
    }
}
package com.sgg.buffer;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Arrays;

/**
 * @description: ScatteringAndGatheringTest
 * Scattering 将数据写入到buffer时,可以采用buffer数据依次写 分散
 * Gatherging 从buffer读取数据时,可以采用buffer数组,依次读 聚合
 *
 * byteRead=8
 * position=5,limit=5
 * position=3,limit=3
 * byteRead=8   byteWrite=8   messageLength=8
 *
 * cmd
 * telnet 127.0.0.1 7000
 * send hell000
 *
 * @date : 2020/6/6 21:46
 * @author: zwz
 */
public class ScatteringAndGatheringTest {
    public static void main(String[] args) throws IOException {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        InetSocketAddress inetSocketAddress = new InetSocketAddress(7000);

        //绑定端口到socket并启动
        serverSocketChannel.socket().bind(inetSocketAddress);
        ByteBuffer[] byteBuffers = new ByteBuffer[2];
        byteBuffers[0] = ByteBuffer.allocate(5);
        byteBuffers[1] = ByteBuffer.allocate(3);

        //等待客户端连接 telnet
        SocketChannel socketChannel = serverSocketChannel.accept();
        int messageLength = 8; //从客户端接收8个字节
        //循环读取
        while (true) {
            long byteRead = 0;
            while (byteRead < messageLength) {
                //从SocketChannel中读取数据到byteBuffers.从position处开始写
                long read = socketChannel.read(byteBuffers);
                byteRead += read;
                System.out.println("byteRead=" + byteRead);
                //使用流打印,看看当前的这个buffer的position和limit
                Arrays.stream(byteBuffers).map(byteBuffer ->
                        "position=" + byteBuffer.position() + ",limit=" + byteBuffer.limit())
                        .forEach(System.out::println);
            }

            //limit = position,position = 0
            Arrays.asList(byteBuffers).forEach(Buffer::flip);
            //将数据读出显示到客户端
            long byteWrite = 0;
            while (byteWrite < messageLength) {
                //write方法使得byteBuffer的position到limit中的元素写入通道中
                long write = socketChannel.write(byteBuffers);
                byteWrite += write;
            }

            //复位
            Arrays.asList(byteBuffers).forEach(Buffer::clear);
            System.out.println("byteRead=" + byteRead + "   byteWrite=" + byteWrite + "   messageLength=" + messageLength);
        }
    }

}

7. channel


7.1 FileChannel


字符串写入本地文件
package com.sgg.channel;

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

/**
 * @description:
 * @date : 2020/6/6 20:21
 * @author: zwz
 */
public class NIOFileChannel01 {
    public static void main(String[] args) throws IOException {
        String str = "hello sgg";
        //创建一个输出流
        FileOutputStream fileOutputStream = new FileOutputStream("E:\\CODE\\Architecture\\netty\\netty-all\\file01.txt");
        //通过输出流获取对应的 FileChannel
        //FileChannel 真实的类型时 FileChannelImpl
        FileChannel fileChannel = fileOutputStream.getChannel();

        //创建一个缓冲区 ByteBuffer
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        //将str放入到byteBuffer
        byteBuffer.put(str.getBytes());
        //对byteBuffer进行flip
        byteBuffer.flip();

        //将byteBuffer 数据写入到 fileChannel
        fileChannel.write(byteBuffer);
        fileOutputStream.close();
    }
}

文件流包裹了channel


文件写入程序
package com.sgg.channel;

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

/**
 * @description:
 * @date : 2020/6/6 20:21
 * @author: zwz
 */
public class NIOFileChannel01 {
    public static void main(String[] args) throws IOException {
        String str = "hello sgg";
        //创建一个文件输入流
        File file = new File("E:\\CODE\\Architecture\\netty\\netty-all\\file01.txt");
        FileInputStream inputStream = new FileInputStream(file);

        //通过fileInputStream获取对应的FileChannel 实际类型 FileChannelImpl
        FileChannel channel = inputStream.getChannel();

        //创建缓冲区
        ByteBuffer buffer = ByteBuffer.allocate((int) file.length());

        //将通道的数据读入到buffer
        channel.read(buffer);

        //将buffer的字节数据转为string
        System.out.println(new String(buffer.array()));
    }
}

文件拷贝

package com.sgg.channel;

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

/**
 * @description:
 * @date : 2020/6/6 20:21
 * @author: zwz
 */
public class NIOFileChannel03 {
    public static void main(String[] args) throws IOException {
        //创建一个输出流
        FileOutputStream fileOutputStream = new FileOutputStream("E:\\CODE\\Architecture\\netty\\netty-all\\file02.txt");
        //通过输出流获取对应的 FileChannel
        //FileChannel 真实的类型时 FileChannelImpl
        FileChannel fileChannel = fileOutputStream.getChannel();

        //创建一个文件输入流
        File file = new File("E:\\CODE\\Architecture\\netty\\netty-all\\file01.txt");
        FileInputStream inputStream = new FileInputStream(file);
        //通过fileInputStream获取对应的FileChannel 实际类型 FileChannelImpl
        FileChannel channel = inputStream.getChannel();

        //创建一个缓冲区 ByteBuffer
        ByteBuffer byteBuffer = ByteBuffer.allocate(512);
        while (true) {
            //将byteBuffer的标记位重置
            byteBuffer.clear();
            int read = channel.read(byteBuffer);
            System.out.println("read: " + read);
            if (read == -1) {//-1表示读取完毕
                break;
            }
            //将byteBuffer 数据写入到 fileChannel
            byteBuffer.flip();
            fileChannel.write(byteBuffer);
        }

        inputStream.close();
        fileOutputStream.close();
    }
}

文件拷贝 transFrom

package com.sgg.channel;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;

/**
 * @description:
 * @date : 2020/6/6 20:21
 * @author: zwz
 */
public class NIOFileChannel04 {
    public static void main(String[] args) throws IOException {
        //创建一个输出流
        FileOutputStream fileOutputStream = new FileOutputStream("E:\\CODE\\Architecture\\netty\\netty-all\\2.png");
        //通过输出流获取对应的 FileChannel
        //FileChannel 真实的类型时 FileChannelImpl
        FileChannel destChannel = fileOutputStream.getChannel();

        //创建一个文件输入流
        File file = new File("E:\\CODE\\Architecture\\netty\\netty-all\\redis持久化的意义.png");
        FileInputStream inputStream = new FileInputStream(file);
        //通过fileInputStream获取对应的FileChannel 实际类型 FileChannelImpl
        FileChannel sourceChannel = inputStream.getChannel();

        //使用transfrom完成拷贝
        destChannel.transferFrom(sourceChannel, 0, sourceChannel.size());

        sourceChannel.close();
        destChannel.close();
        inputStream.close();
        fileOutputStream.close();
    }
}

注意

7.2

7.3

8. Selector 选择器





  1. 当客户端连接时,会通过serverSocketChannel 得到 socketChannel
  2. 将socketChannel注册到selector上,register。selector上可以注册多个SocketChannel
  3. 注册后返回一个SelectionKey,会和该Selector关联(集合)
  4. Selector 进行监听Select方法,返回有事件发生的通道的个数。
  5. 进一步得到各个SelectionKey(有事件发生的)
  6. 在通过selectionKey反向获取socketChannel,方法channel()
  7. 可以通过得到的channel,完成业务处理



相关文章

  • 2. Java NIO 概述

    Java NIO由下面几个核心组件组成: Channel Buffer Selector Java NIO有更多的...

  • Netty之NIO

    ------NIO简介(1)-------- NIO组件 channel,buffer,selector,pip,...

  • Java NIO之Channel

    本文开始讲解Java NIO 的三个核心组件,Channel,Buffer,Selector。先从Channel开...

  • NIO java编程

    NIO 同步式非阻塞式IO NIO组件:Buffer channel selector Buffer 缓冲区 1....

  • Java NIO

    java Nio Selector 选择器Buffer 缓冲器Channel 通道 Selector是NIO的核心...

  • Netty基础-NIO(一)

    零、本文纲要 一、NIO三大组件 Channel Buffer Selector 二、Buffer 基础依赖 By...

  • NIO核心组件 channel、buffer、selector

    1. 三个核心组件 channel、buffer、selector 每个channel都会对应一个buffer s...

  • Java NIO三剑客—Buffer、Channel、Selec

    [TOC]Java NIO包含三个核心部分: Buffer Channel Selector Buffer即缓冲区...

  • Buffer详解

    Buffer简介 在Java NIO中,主要有三大基本的组件:Buffer、Channel和Selector,上一...

  • NIO 总结

    核心部分: Buffer、Channel、Selector NIO操作面向缓冲区,数据从Channel读取到Buf...

网友评论

      本文标题:NIO核心组件 channel、buffer、selector

      本文链接:https://www.haomeiwen.com/subject/acdftktx.html