NIO

NIO

import org.junit.Test;

import java.io.*;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.*;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Date;
import java.util.Iterator;
import java.util.Scanner;

/**
 * @author 小邱
 * @version 0.0.1
 * @description NIOTest
 * @since 2021/9/10 16:02
 */
public class NIOTest {
    /*
        Java NIO系统的核心在于:通道(Channel)和缓冲区 (Buffer)
            通道表示打开到IO设备(例如:文件、套接字)的连接。若需要使用NIO系统,需要获取用于连接IO设备的通道以及用于容纳数据的缓冲区。
            然后操作缓冲区,对数据进行处理。
            Channel 负责传输, Buffer 负责存储
        缓冲区(Buffer):基本数据类型的数组(boolean 除外)
            ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer
            都是通过如下方法获取一个 Buffer 对象:
            static XxxBuffer allocate(int capacity):创建一个容量为 capacity的XxxBuffer对象
        核心方法:get() 与 put() 方法
        核心属性:
            容量 (capacity):表示Buffer最大数据容量,缓冲区容量不能为负,并且创建后不能更改。
            限制 (limit):位于limit后的数据不可读写。缓冲区的限制不能为负,并且不能大于其容量。
            位置 (position):下一个要读取或写入的数据的索引。缓冲区的位置不能为负,并且不能大于其限制
            标记 (mark)与重置 (reset):标记是一个索引,通过Buffer中的mark()方法指定Buffer中一个特定的position,之后可以通过调用reset()方法恢复到这个position.
            标记、位置、限制、容量遵守以下不变式:
                0 <= mark <= position <= limit <= capacity
         非直接缓冲区:allocate()创建JVM缓冲区           效率低                   占用资源较少,容易被释放
         直接缓冲区:allocateDirect()创建物理内存缓冲区   效率高(减少内部复制步骤)    分配资源消耗大(占内存),不易回收(不安全)
         通道(channel):用于源节点与目标节点的连接,负责缓冲区中数据传输,本身不存储数据,需缓冲区配合传输。
            主要实现类:
                FileChannel:用于读取、写入、映射和操作文件的通道。
                DatagramChannel:通过 UDP 读写网络中的数据通道。
                SocketChannel:通过 TCP 读写网络中的数据。
                ServerSocketChannel:可以监听新进来的 TCP 连接,对每一个新进来的连接都会创建一个 SocketChannel。
            获取通道:
                支持通道的对象
                    本地IO:FileInputStream、FileOutputStream、RandomAccessFile
                    网络IO:DatagramSocket、 Socket、ServerSocket
                调用 getChannel()
                静态方法open()
                Files工具类的newByteChannel()
            通道之间的数据传输
                transferForm()
                transferTo()
        分散读取(Scattering Reads)是指从Channel中读取的数据“分散”到多个Buffer中。
            注意:按照缓冲区的顺序,从Channel中读取的数据依次将Buffer填满。
        聚集写入(Gathering Writes)是指将多个Buffer中的数据“聚集”到Channel。
            注意:按照缓冲区的顺序,写入position和limit之间的数据到 Channel 。
        选择器(Selector):SelectableChannel的多路复用器,用于监控通道的IO状况。



     */
    @Test
    public void test1() {
        //1、分配一个指定大小的缓冲区
        ByteBuffer buf = ByteBuffer.allocate(1024);
        ByteBuffer buff = ByteBuffer.allocateDirect(1024);

        System.out.println(buf.isDirect());//false(非直接缓冲区)
        System.out.println(buff.isDirect());//true(直接缓冲区)

        System.out.println("-----------allocate()------------");
        System.out.println(buf.position());//0
        System.out.println(buf.limit());//1024
        System.out.println(buf.capacity());//1024

        //2、利用put()存入数据到缓冲区
        String str = "abcde";
        buf.put(str.getBytes());

        System.out.println("-----------put()------------");
        System.out.println(buf.position());//5
        System.out.println(buf.limit());//1024
        System.out.println(buf.capacity());//1024

        //3、切换到读取模式
        buf.flip();

        System.out.println("-----------flip()------------");
        System.out.println(buf.position());//0
        System.out.println(buf.limit());//5
        System.out.println(buf.capacity());//1024

        //4、利用get()从缓冲区中读取数据
        byte[] dest = new byte[buf.limit()];
        buf.get(dest);
        System.out.println(new String(dest, 0, dest.length));//abcde

        System.out.println("-----------get()------------");
        System.out.println(buf.position());//5
        System.out.println(buf.limit());//5
        System.out.println(buf.capacity());//1024

        //5、rewind():可重复读
        buf.rewind();

        System.out.println("-----------rewind()------------");
        System.out.println(buf.position());//0
        System.out.println(buf.limit());//5
        System.out.println(buf.capacity());//1024

        //6、mark()标记/reset()重置
        buf.get(dest, 0, 2);

        System.out.println("-----------mark()/reset()------------");
        System.out.println(new String(dest, 0, 2));//ab
        System.out.println(buf.position());//2

        buf.mark();//标记
        buf.get(dest, 2, 2);

        System.out.println("-----------mark()------------");
        System.out.println(new String(dest, 2, 2));//cd
        System.out.println(buf.position());//4

        buf.reset();//重置

        System.out.println("-----------reset()------------");
        System.out.println(buf.position());//2

        //7、hasRemaining()判断缓冲区中是否有剩余的数量
        if (buf.hasRemaining()) {
            //获取缓冲区中可以操作的数量
            System.out.println(buf.remaining());//3
        }

        //8、clear()清空缓冲区,但缓冲区中的数据依然存在,只是处于“被遗忘“状态,无法正确读取数据
        buf.clear();

        System.out.println("-----------clear()------------");
        System.out.println(buf.position());//0
        System.out.println(buf.limit());//1024
        System.out.println(buf.capacity());//1024

        System.out.println((char) buf.get());//a


    }

    @Test
    public void test2() {
        //利用通道完成文件复制(非直接缓冲区)
        //1、创建输入流和输出流,指定文件
        FileInputStream fis = null;
        FileOutputStream fos = null;
        FileChannel in = null;
        FileChannel out = null;
        try {
            fis = new FileInputStream("F:\\Lean\\Text\\src\\main\\resources\\1.jpg");
            fos = new FileOutputStream("F:\\Lean\\Text\\src\\main\\resources\\2.jpg");
            //2、获取通道
            in = fis.getChannel();
            out = fos.getChannel();
            //3、分配指定大小的缓冲区
            ByteBuffer buf = ByteBuffer.allocate(1024);
            //4、将通道中数据存入缓冲区
            while (in.read(buf) != -1) {
                //5、切换读模式
                buf.flip();
                //6、将缓冲区的数据写入通道
                out.write(buf);
                //7、清空缓冲区
                buf.clear();
            }


        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
                if (out != null) {
                    out.close();
                }
                if (fis != null) {
                    fis.close();
                }
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Test
    public void test3() {
        //利用通道完成文件复制(直接缓冲区)
        FileChannel inChannel = null;
        FileChannel outChannel = null;
        try {
            //获取通道
            inChannel = FileChannel.open(Paths.get("F:\\Lean\\Text\\src\\main\\resources\\1.jpg"), StandardOpenOption.READ);
            outChannel = FileChannel.open(Paths.get("F:\\Lean\\Text\\src\\main\\resources\\3.jpg"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
            //内存映射文件
            MappedByteBuffer inMap = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
            MappedByteBuffer outMap = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());
            //直接对缓冲区进行数据的读写操作
            byte[] dest = new byte[inMap.limit()];
            inMap.get(dest);
            outMap.put(dest);

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

    @Test
    public void test4() throws IOException {
        //通道之间的数据传输(直接缓冲区)
        FileChannel in = FileChannel.open(Paths.get("F:\\Lean\\Text\\src\\main\\resources\\1.jpg"), StandardOpenOption.READ);
        FileChannel out = FileChannel.open(Paths.get("F:\\Lean\\Text\\src\\main\\resources\\4.jpg"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);

        in.transferTo(0, in.size(), out);
        //out.transferFrom(in,0,in.size());

        in.close();
        out.close();


    }

    @Test
    public void test5() throws IOException {
        RandomAccessFile raf = new RandomAccessFile("F:\\Lean\\Text\\src\\main\\resources\\IDEA快捷键.txt", "rw");

        //获取通道
        FileChannel channel = raf.getChannel();
        //分配指定大小的缓冲区
        ByteBuffer buf1 = ByteBuffer.allocate(100);
        ByteBuffer buf2 = ByteBuffer.allocate(1024);
        //分散读取
        ByteBuffer[] buf = {buf1, buf2};
        channel.read(buf);

        for (ByteBuffer byteBuffer : buf) {
            byteBuffer.flip();

        }
        System.out.println(new String(buf[0].array(), 0, buf[0].limit()));
        System.out.println("------------------------------");
        System.out.println(new String(buf[1].array(), 0, buf[1].limit()));

        //聚集写入
        RandomAccessFile rdaf = new RandomAccessFile("F:\\Lean\\Text\\src\\main\\resources\\IDEA快捷键1.txt", "rw");
        FileChannel channel1 = rdaf.getChannel();

        channel1.write(buf);
    }

    //阻塞网络通信
    @Test
    //客户端
    public void test6() throws IOException {
        //1、获取通道
        SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("localhost", 9898));

        FileChannel infileChannel = FileChannel.open(Paths.get("F:\\Lean\\Text\\src\\main\\resources\\1.jpg"), StandardOpenOption.READ);

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

        //3、读取本地文件,并发送到服务端
        while ((infileChannel.read(buf) != -1)) {
            buf.flip();
            socketChannel.write(buf);
            buf.clear();
        }
        socketChannel.shutdownOutput();
        //4、接收服务端的反馈
        int len = 0;
        while ((len = socketChannel.read(buf)) != -1) {
            buf.flip();
            System.out.println(new String(buf.array(), 0, len));
            buf.clear();
        }
        //5、关闭通道
        infileChannel.close();
        socketChannel.close();
    }

    @Test
    //服务端
    public void test7() throws IOException {
        //1、获取通道
        ServerSocketChannel ssc = ServerSocketChannel.open();

        FileChannel out = FileChannel.open(Paths.get("F:\\Lean\\Text\\src\\main\\resources\\5.jpg"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);

        //2、绑定连接
        ssc.bind(new InetSocketAddress(9898));

        //3、获取客户端连接的通道
        SocketChannel socketChannel = ssc.accept();

        //4、分配指定大小的缓冲区
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);


        //5、接受客户端的数据,并保存到本地
        while ((socketChannel.read(byteBuffer)) != -1) {
            byteBuffer.flip();
            out.write(byteBuffer);
            byteBuffer.clear();
        }
        //6、发送反馈
        byteBuffer.put("服务器接收数据成功".getBytes());
        byteBuffer.flip();
        socketChannel.write(byteBuffer);
        //7、关闭通道
        socketChannel.close();
        out.close();
        ssc.close();
    }

    //非阻塞网络通信
    @Test
    //客户端
    public void test8() throws IOException {
        //1、获取通道
        SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("localhost", 9898));

        //2、切换成非阻塞模式
        socketChannel.configureBlocking(false);

        //3、分配指定大小的缓冲区
        ByteBuffer buf = ByteBuffer.allocate(1024);

        //4、读取本地文件,并发送到服务端
        FileChannel infileChannel = FileChannel.open(Paths.get("F:\\Lean\\Text\\src\\main\\resources\\IDEA快捷键.txt"), StandardOpenOption.READ);

        while ((infileChannel.read(buf) != -1)) {
            buf.flip();
            socketChannel.write(buf);
            buf.clear();
        }

        //5、关闭通道
        infileChannel.close();
        socketChannel.close();
    }

    @Test
    //服务端
    public void test9() throws IOException {
        //1、获取通道
        ServerSocketChannel ssc = ServerSocketChannel.open();
        //2、切换非阻塞模式
        ssc.configureBlocking(false);
        //3、绑定连接
        ssc.bind(new InetSocketAddress(9898));
        //4、获取选择器
        Selector selector = Selector.open();
        //5、将通道注册到选择器上,指定监听事件
        ssc.register(selector, SelectionKey.OP_ACCEPT);
        //6、轮询式的获取已经“准备就绪”的事件
        while (selector.select() > 0) {
            //7、获取当前选择器中所有的选择键(已经准备就绪的监听事件)
            Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
            while (keyIterator.hasNext()) {
                //8、获取已经准备就绪的事件
                SelectionKey key = keyIterator.next();
                //9、判断什么事件准备就绪
                if (key.isAcceptable()) {
                    //10、若接受就绪,获取客户端连接
                    SocketChannel accept = ssc.accept();
                    //11、切换非阻塞式
                    accept.configureBlocking(false);
                    //12、将该通道注册到选择器上
                    accept.register(selector, SelectionKey.OP_READ);
                } else if (key.isReadable()) {
                    //13、获取当前选择器读就绪的通道
                    SocketChannel channel = (SocketChannel) key.channel();
                    //14、读取数据
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    int len = 0;
                    while ((len = channel.read(buffer)) > 0){
                        buffer.flip();
                        System.out.println(new String(buffer.array(),0,len));
                        buffer.clear();
                    }
                }
                //15、取消选择器
                keyIterator.remove();
            }

        }
        //7、关闭通道
        ssc.close();
    }

    //DatagramChannel非阻塞网络通信
    @Test
    public void test10() throws IOException {
        DatagramChannel dc = DatagramChannel.open();
        dc.configureBlocking(false);
        ByteBuffer buf =ByteBuffer.allocate(1024);
        Scanner scan = new Scanner(System.in);
        while (scan.hasNext()){
            String str  = scan.next();
            buf.put((new Date().toString() + "\n" +str).getBytes());
            buf.flip();
            dc.send(buf,new InetSocketAddress("localhost",9898));
            buf.clear();
        }
        dc.close();

    }

    @Test
    public void test11() 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<SelectionKey> it = selector.selectedKeys().iterator();
            while (it.hasNext()){
                SelectionKey sk = it.next();
                if (sk.isReadable()){
                    ByteBuffer buf = ByteBuffer.allocate(1024);
                    dc.receive(buf);
                    buf.flip();
                    System.out.println(new String(buf.array(),0,buf.limit()));
                    buf.clear();
                }
            }
            it.remove();
        }

    }


}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值