NIO:基本操作和学习

NIO的核心:缓冲区,通道,选择器 

 

Table of Contents

一、缓冲区 Buffer  :存储数据

1 基本概念:

2 直接缓冲区和非直接缓冲区

3 种类:基本数据类型处理boolean外都有

4 常用方法

二、管道channel  : 传输数据

1 概念

2 通道的主要实现类

3 获取的三种方式

 例子:通过非直接缓冲区完成文件复制

 通过直接缓冲区完成文件复制(内存映射文件)

4  通道之间传输 transferTo和 transferFrom

5 分散(scatter)和聚集(Gather)

6 字符集charset

三、阻塞io和非阻塞io

1 阻塞io,就是服务端一直等待客户端的请求,不能去做其他的事情

2 示例

   实例一

示例二

2 非阻塞io,服务端可以在选择器上监控已经准备就绪的事件处理,不用一直等待

示例一

示例二

四、管道:


一、缓冲区 Buffer  :存储数据

1 基本概念:

  1 position :当前操作数据的位置,指针位置

   2 limit:缓冲区中可以操作数据的大小,即limit后面的数据不能操作

   3 capacity:缓冲区的存储大小

2 直接缓冲区和非直接缓冲区

 

   非直接缓冲区(allocate())方法分配的缓冲区,将缓冲区直接建立在jvm上面

   直接缓冲区:allocateDirect()方法分配的缓冲区,将缓冲区直接建立在磁盘内存中,可以提高效率,弊端是消耗大,不安全

ByteBuffer.allocateDirect(1024);
ByteBuffer allocate = ByteBuffer.allocate(1024);

3 种类:基本数据类型处理boolean外都有

ByteBuffer
CharBuffer

ShortBuffer

IntBuffer

LongBuffer

FloatBuffer
​​​​​DoubleBuffer

4 常用方法

 

常用方法:
put  存储数据
get  获取数据
flip:转换为读取模式,将指针位置置于开始部位可以读或者写操作
clear 清空位子,数据还在

mark 标记此刻数据的位子
reset  回到mark标记的数据位子
 ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
        ByteBuffer allocate = ByteBuffer.allocate(1024);
        System.out.println(allocate.position());
        System.out.println(allocate.limit());
        System.out.println(allocate.capacity());


        //存数据
        String str = "abcde";
        allocate.put(str.getBytes());
        System.out.println("*************put***********");
        System.out.println(allocate.position());
        System.out.println(allocate.limit());
        System.out.println(allocate.capacity());


        System.out.println("******************flip************");
        allocate.flip();//切换为读数据模式

        System.out.println("*************put***********");
        System.out.println(allocate.position());
        System.out.println(allocate.limit());
        System.out.println(allocate.capacity());

        System.out.println("**********get***************");
        byte[] dest = new byte[allocate.limit()];
        allocate.get(dest);
        System.out.println(new String(dest,0,dest.length));

        System.out.println(allocate.position());
        System.out.println(allocate.limit());
        System.out.println(allocate.capacity());

        System.out.println("*************clear******");
        allocate.clear();

        System.out.println(allocate.position());
        System.out.println(allocate.limit());
        System.out.println(allocate.capacity());

        System.out.println("***********mark**********");

        allocate.mark();
        System.out.println(allocate.position());
        System.out.println(allocate.limit());
        System.out.println(allocate.capacity());

        allocate.reset();

 

二、管道channel  : 传输数据

1 概念

    表示IO源与目标打开的连接,类似于传统的流,只不过channel本身不存储数据,它只能与缓冲区交互。

用于源节点和目标节点的连接,在java NIO中负责缓冲区中数据的传输。

2 通道的主要实现类

java.nio.channels.Channel接口
    -- FileChannel
    -- SocketChannel
    -- ServerSocketChannel
    -- DatagramChannel

    -- Pipe.SourceChannel
    --Pipe.SinkChannel

3 获取的三种方式

 

1 java针对支持通道的类提供了getChannel()方法
本地IO
  FileInputStream/FileOutputStream
  RandomAccessFile

 网络IO
  socket
  ServerSocket
  DatagramSocket

比如:
RandomAccessFile raf2 = new RandomAccessFile("2.jpg", "rw"); FileChannel outChannel = raf2.getChannel();

2 在jdk1.7 中NIO2针对各个通道提供了静态方法open():

比如:

FileChannel.open(Paths.get("G:\\360Downloads\\photos\\0.jpg"),StandardOpenOption.READ);
3 在jdk1.7 中NIO2的Files 工具类的newByteChannel()

 例子:通过非直接缓冲区完成文件复制

 /**
     * 利用通道完成数据复制
     */
    @Test
    public void test01(){

        FileInputStream inputStream =  null;
        FileOutputStream outputStream = null;
        FileChannel inChannel = null;
        FileChannel outChannel = null;

        try {
            inputStream = new FileInputStream("src/1.jpg");
            outputStream = new FileOutputStream("2.jpg");

            //1 获取通道
            inChannel = inputStream.getChannel();
            outChannel = outputStream.getChannel();

            //2 分配指定大小的缓冲区
            ByteBuffer buff = ByteBuffer.allocate(1024);

            //3 将通道的数据读入缓冲区

            while(inChannel.read(buff) != -1){
                buff.flip();
                //4 将缓冲区的数据写入通道
                outChannel.write(buff);

                buff.clear();
            }

            //5 关闭流和通道
        } catch (IOException e) {
            e.printStackTrace();
        } finally {

            if(inChannel != null) {
                try {
                    inChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if(outChannel!=null){
                try {
                    outChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if(outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if(inputStream!=null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }



        }




    }

 

 通过直接缓冲区完成文件复制(内存映射文件)

  /**
     * 使用直接缓冲区完成文件复制(内存映射文件)
     */
    @Test
    public void test02() throws Exception {
        FileChannel inChannel = FileChannel.open(Paths.get("G:\\360Downloads\\photos\\0.jpg"),StandardOpenOption.READ);

        FileChannel outChannel = FileChannel.open(Paths.get("G:\\360Downloads\\photos\\00.jpg"), StandardOpenOption.READ, StandardOpenOption.WRITE,StandardOpenOption.CREATE);

        //内存映射文件
        MappedByteBuffer inBuffer =  inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());

        MappedByteBuffer outBuffer = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());

        //直接对缓冲区进行数据读写操作
        byte[] dst = new byte[inBuffer.limit()];
        inBuffer.get(dst);
        outBuffer.put(dst);


        outChannel.close();
        inChannel.close();


    }

4  通道之间传输 transferTo和 transferFrom

 /*
     * 通道之间传输数据
     * transferTo
     * transferFrom
     */
    @Test
    public void test03() throws IOException {
        FileChannel inChannel = FileChannel.open(Paths.get("G:\\360Downloads\\photos\\0.jpg"),StandardOpenOption.READ);

        FileChannel outChannel = FileChannel.open(Paths.get("G:\\360Downloads\\photos\\00.jpg"), StandardOpenOption.READ, StandardOpenOption.WRITE,StandardOpenOption.CREATE);

        //transferTo
//        inChannel.transferTo(0,inChannel.size(),outChannel);

        //transferFrom
        outChannel.transferFrom(inChannel,0,inChannel.size());

        inChannel.close();
        outChannel.close();


    }

5 分散(scatter)和聚集(Gather)

/**
     * 分散和聚集
     */
    @Test
    public void test04() throws Exception {

        //1 获取通道
        RandomAccessFile raf1 = new RandomAccessFile("1.jpg", "rw");

        FileChannel inChannel = raf1.getChannel();

        //2 分配指定缓冲区

        ByteBuffer buffer1 = ByteBuffer.allocate(100);
        ByteBuffer buffer2 = ByteBuffer.allocate(102400);
        ByteBuffer buffer3 = ByteBuffer.allocate(102400);
        ByteBuffer buffer4 = ByteBuffer.allocate(102400);


        //3 分散du读1取
        ByteBuffer[] dst = {
                buffer1,buffer2,buffer3,buffer4
        };
        inChannel.read(dst);

        for (ByteBuffer b : dst){
            b.flip();
        }

        // 4 聚集写出
        RandomAccessFile raf2 = new RandomAccessFile("2.jpg", "rw");
        FileChannel outChannel = raf2.getChannel();

        outChannel.write(dst);

        outChannel.close();
        inChannel.close();
    }

6 字符集charset

 @Test
    public void test07() throws CharacterCodingException {

        Charset charset = Charset.forName("GBK");

        //获取编码器和解解码器

        CharsetEncoder encoder = charset.newEncoder();
        CharsetDecoder decoder = charset.newDecoder();


        CharBuffer buffer = CharBuffer.allocate(1024);
        buffer.put("研发费分为非") ;
        buffer.flip();

        //编码
        ByteBuffer byteBuffer = encoder.encode(buffer);
        //解码
        CharBuffer charBuffer = decoder.decode(byteBuffer);
        //读取

        System.out.println(charBuffer.toString());


    }

三、阻塞io和非阻塞io

1 阻塞io,就是服务端一直等待客户端的请求,不能去做其他的事情

2 示例

   实例一

 @Test
    public void client() throws IOException {

        //1 获取通道
        SocketChannel skChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",9898));

        FileChannel fileChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
        //2 获取缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        //3 通道传输出去数据
        while(fileChannel.read(buffer) != -1){
            buffer.flip();
            skChannel.write(buffer);
            buffer.clear();
        }

        //4 关闭流
        fileChannel.close();
        skChannel.close();


    }
    @Test
    public void server() throws IOException {

        // 获取通道
        ServerSocketChannel sskChannel = ServerSocketChannel.open();
        // 绑定连接
        sskChannel.bind(new InetSocketAddress(9898));
        //获取客户端连接通道
        SocketChannel skChannel = sskChannel.accept();

        //获取缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        //接收客户端数据并存入本地
        FileChannel outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);

        while(skChannel.read(buffer) != -1){
            buffer.flip();
            outChannel.write(buffer);
            buffer.clear();
        }

        outChannel.close();
        skChannel.close();
        sskChannel.close();


    }

示例二

@Test
    public void client() throws IOException {

        //1 获取通道
        SocketChannel skChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",9898));

        FileChannel fileChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
        //2 获取缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        //3 通道传输出去数据
        while(fileChannel.read(buffer) != -1){
            buffer.flip();
            skChannel.write(buffer);
            buffer.clear();
        }

        System.out.println("发送图片完成");
        //阻塞式必须告诉服务端发送完成服务端才可以接收到数据
        skChannel.shutdownOutput();
        //接收服务端反馈
        int len = 0;
        while((len = skChannel.read(buffer)) != -1){
            buffer.flip();
            System.out.println(new String(buffer.array(),0,len));
            buffer.clear();
        }

        //4 关闭流
        fileChannel.close();
        skChannel.close();


    }
    @Test
    public void server() throws IOException {

        // 获取通道
        ServerSocketChannel sskChannel = ServerSocketChannel.open();
        // 绑定连接
        sskChannel.bind(new InetSocketAddress(9898));
        //获取客户端连接通道
        SocketChannel skChannel = sskChannel.accept();

        //获取缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        //接收客户端数据并存入本地
        FileChannel outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);

        while(skChannel.read(buffer) != -1){
            buffer.flip();
            outChannel.write(buffer);
            buffer.clear();
        }

        System.out.println("接收图片完成");

        byte[] src = "获取客户端图片成功".getBytes();
        buffer.put(src);

        buffer.flip();
        skChannel.write(buffer);

        outChannel.close();
        skChannel.close();
        sskChannel.close();


    }

2 非阻塞io,服务端可以在选择器上监控已经准备就绪的事件处理,不用一直等待

示例一

 public static void main(String[] args) throws IOException {
        //1 获取通道
        SocketChannel skChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));

        //切换 为非阻塞式
        skChannel.configureBlocking(false);

        // 2 获取缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024);

        Scanner scan = new Scanner(System.in);

        while(scan.hasNext()) {
            String str = scan.next();
            System.out.println(str);
            //3 缓冲区存储数据
            buffer.put((new Date()+"\n"+str).getBytes());
            //4 通道传输数据
            buffer.flip();
            skChannel.write(buffer);
            buffer.clear();

        }

        // 关闭通道
        skChannel.close();
    }
    @Test
    public  void client() throws IOException {

        //1 获取通道
        SocketChannel skChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));

        //切换 为非阻塞式
        skChannel.configureBlocking(false);

        // 2 获取缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024);



        //3 缓冲区存储数据
        buffer.put(new Date().toString().getBytes());
        //4 通道传输数据
        buffer.flip();
        skChannel.write(buffer);
        buffer.clear();

        // 关闭通道
        skChannel.close();

    }

    @Test
    public void server() throws IOException {
        //获取通道
        ServerSocketChannel sskChannel = ServerSocketChannel.open();
        // 切换为非阻塞式
        sskChannel.configureBlocking(false);

        //绑定客户端连接
        sskChannel.bind(new InetSocketAddress(9898));

        //获取选择器
        Selector selector = Selector.open();

        // 将通道注册到选择器
        SelectionKey selectionKey = sskChannel.register(selector, SelectionKey.OP_ACCEPT);
//        System.out.println("tongdao 注册选择器");
        //轮询式的获取选择器上已经准备就绪的事件

        while(selector.select()>0){

            // 获取当前选择器中所有的选择键(已经准备就绪的事件)
            Iterator iterator = selector.selectedKeys().iterator();

            while(iterator.hasNext()){
                //获取准备就绪的事件
                SelectionKey next = iterator.next();
//                System.out.println("获取选择器"+next.isReadable()+"||"+next.isAcceptable());
                if (next.isAcceptable()) {
                    //接收事件
                    //获取客户端连接的通道,并注册选择器
                    SocketChannel sChannel = sskChannel.accept();
                    //切换为非阻塞
                    sChannel.configureBlocking(false);
                    //注册到选择器
                    sChannel.register(selector,SelectionKey.OP_READ);

                }else if(next.isReadable()){
                    //读事件
                    //获取选择器上(读状态就绪)状态的通道
                    SocketChannel socketChannel = (SocketChannel) next.channel();
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    int len = 0;
                    while((len=socketChannel.read(buffer)) !=-1){
                        buffer.flip();
                        System.out.println(new String(buffer.array(),0,len));
                        buffer.clear();
                    }

                }
            }
            //需要将这个从选择器中取消掉,不然下次循环还会获取准备就绪的这个事件,重复获取了 Iterator iterator = selector.selectedKeys().iterator();
            iterator.remove();
        }
    }

示例二

  @Test
    public void client() throws IOException {

        DatagramChannel dc = DatagramChannel.open();
        dc.configureBlocking(false);

        ByteBuffer buffer = ByteBuffer.allocate(1024);

        buffer.put("ceshi DatagramChannel".getBytes());

        buffer.flip();

        dc.send(buffer,new InetSocketAddress("127.0.0.1",9898));

        buffer.clear();

        dc.close();
    }


    @Test
    public void server() throws IOException {
        DatagramChannel dc = DatagramChannel.open();
        dc.configureBlocking(false);

        dc.bind(new InetSocketAddress(9898));

        Selector selector = Selector.open();

        dc.register(selector, SelectionKey.OP_READ);

        while (selector.select() > 0) {
            //遍历获取准备好的事件

            Iterator iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                SelectionKey next = iterator.next();
                //只需要监控读事件
                if (next.isReadable()) {
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    dc.receive(buffer);

                    buffer.flip();
                    System.out.println(new String(buffer.array(), 0, buffer.limit()));
                    buffer.clear();

                }
                iterator.remove();

            }
        }
    }

 

四、管道:

javanio两个线程之间单向的数据传输:管道Pipe有个Source和Sink通道,数据会被写入Sink通道,从Source读取

 @Test
    public void test() throws IOException {

        //获取管道
        Pipe open = Pipe.open();

        //将缓冲区的数据写入管道
        Pipe.SinkChannel sink = open.sink();
        Pipe.SourceChannel source = open.source();

        ByteBuffer buffer = ByteBuffer.allocate(1024);
        buffer.put("测试sink通道".getBytes());
        System.out.println(buffer.position());
        System.out.println(buffer.limit());
        buffer.flip();
        System.out.println(buffer.position());
        System.out.println(buffer.limit());
        sink.write(buffer);

        System.out.println(buffer.position());
        System.out.println(buffer.limit());

        // 读取缓冲区的数据
        //切换操作模式,将位置置为初始位置,才可以读取
        buffer.flip();
        System.out.println(buffer.position());
        System.out.println(buffer.limit());

        source.read(buffer);
        System.out.println(new String(buffer.array(),0,buffer.limit()));


        //关闭通道
        sink.close();
        source.close();

    }

 

你可能感兴趣的:(NIO)