JAVA中的NIO

1 BIO概述

  • BIO
     BIO ==> Basic IO (基本IO), Block IO(阻塞IO)
     Scanner操作,文件读写操作,Socket数据传输操作… 都是BIO
 比如TPC群聊,私聊聊天室
	Socket涉及到的IO,也是BIO
	资源浪费:
		1. 多线程,每一个Socket会对应一个线程,如果用户量巨大,会导致线程过
		多,资源处理过多
		2. 采用阻塞状态,一旦进入阻塞,代码无法执行其他操作。
		3. 承载量一般,吞吐量比较小,同时可靠性不佳

2 NIO概述

  • NIO
     NIO ==> New IO(新IO), Non-Block IO(非阻塞IO)
     NIO非阻塞IO,运行当前程序在处理IO事务时,不会影响其他程序的运行,可以在不使用多线程的情况下,满足IO操作要求。
    • 三大核心部分:
      • 通道
            Channel
            文件操作,网络数据传递操作使用的通道
      • 缓冲
            Buffer
            缓冲使用可以提供操作效率,减少不必要的读写次数
      • 选择器
            Selector
            真·核心 老大 boss
        在这里插入图片描述

3 Buffer Channel完成文件操作

3.1 常用API

java.nio.Buffer

  • Buffer缓冲区
     ByteBuffer 字节缓冲 常用
     ShortBuffer
     IntBuffer
     LongBuffer
     CharBuffer 字节缓冲 常用
     FloatBuffer
     DoubleBuffer

  • 常用方法:
     public static ByteBuffer allocate(int capacity);
      按照指定的字节数分配对应的缓冲区空间,保存字节数据
     public byte get();
      从字节缓冲区对象中读取一个byte类型数组
     public final Buffer flip();
      翻转缓冲区,回到缓冲区的开始位置。
     public static ByteBuffer wrap(byte[] arr);
      存入一个byte类型数组到缓冲区,会得到一个新的ByteBuffer
     public static ByteBuffer put(byte[] b);
      将字节数组存入缓冲去

  • Channel接口,通道接口
     FileChannel 文件操作通道
     DatagramChannel UDP协议数据包操作的Channel
     ServerSocketChannel TCP服务端ServerSocket对应Channel
     SocketChannel TCP客户端Socket对应Channel

  • 首先操作文件,以FileChannel为例
     public long read(ByteBuffer buffer);
      从通道中读取数据到ByteBuffer中
     public long write(ByteBuffer buffer);
      从Buffer中写数据到通道中
     public long transferFrom(ReadableByteChannel src, long position, long count)
     从指定srcChannel中,指定位置position开始,读取count个元素,到当前通道中
     文件复制操作。
     public long transferTo(long position, long count, WritableByteChannel target)
     将当前通道中的数据写入到target中,从当前通道的position位置开始,计数count

3.2 操作文件数据
import org.junit.Test;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;


public class FileNioTest {

    /*
    通过NIO写入数据到文件中的操作
     */
    @Test
    public void testNioFileWrite() throws IOException {
        // 1. 文件操作字节输出流
        FileOutputStream fos = new FileOutputStream("D:/aaa/1.txt");

        // 2. 利用文件操作输出字节流对象获取对应的Channel通道
        FileChannel foc = fos.getChannel();

        // 3. 准备一个缓冲区 4KB缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024 * 4);

        // 4. 准备数据,放入缓冲区
        String str = "测试NIO";
        buffer.put(str.getBytes());

        // 5. 存在缓冲区数据放入之后,缓冲区指针发生改变,到达存入数据的末尾
        // 如果此时调用写入操作,会从存入缓冲区之后开始保存
        // 让缓冲区指针回到最初的起点,并且操作写入程序,只会保存缓冲区内的有效数据
        buffer.flip();

        // 6. 缓冲区数据写入到通道中
        foc.write(buffer);

        // 7. 关闭资源
        fos.close();
    }

    @Test
    public void testNioFileRead() throws IOException {
        // 1. 文件字节操作输入流
        FileInputStream fis = new FileInputStream("D:/aaa/1.txt");

        // 2. FileChannel
        FileChannel fic = fis.getChannel();

        // 3. 准备缓冲
        ByteBuffer buffer = ByteBuffer.allocate(1024);

        // 4. 从Channel读取数据保存到缓冲区中
        int read = fic.read(buffer);
        System.out.println(read);

        // 5. 展示数据
        // String(byte[] arr, int off, int count)
        System.out.println(new String(buffer.array(), 0, read));

        // 6. 关闭资源
        fis.close();
    }

    // 130
    @Test
    public void testCopyFile() throws IOException {
        long start = System.currentTimeMillis();
        // 1. 安排输出流和输入流
        FileInputStream fis = new FileInputStream("D:/aaa/1.mp4");
        FileOutputStream fos = new FileOutputStream("D:/aaa/2.mp4");

        // 2. 准备两个Channel
        FileChannel srcChannel = fis.getChannel();
        FileChannel dstChannel = fos.getChannel();

        // 3. 拷贝方法
        srcChannel.transferTo(0, srcChannel.size(), dstChannel);
        // dstChannel.transferFrom(srcChannel, 0, srcChannel.size());

        // 4. 关闭资源
        fos.close();
        fis.close();

        long end = System.currentTimeMillis();
        System.out.println("Time:" + (end - start));
    }

    // 300
    @Test
    public void testCopyUseBuffer() throws IOException {
        long start = System.currentTimeMillis();
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:/aaa/1.mp4"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:/aaa/3.mp4"));

        int length = -1;
        byte[] buf = new byte[4 * 1024];

        while ((length = bis.read(buf)) != -1) {
            bos.write(buf, 0, length);
        }

        bos.close();
        bis.close();
        long end = System.currentTimeMillis();
        System.out.println("Time:" + (end - start));
    }
}

4 网络编程使用NIO【重点】

4.1 Selector选择器老大
  • Selector
     选择器,网络编程使用NIO的大哥!!!
     服务器可以执行一个线程,运行Selector程序,进行监听操作。
     新连接, 已经连接, 读取数据,写入数据
  • Selector常用方法:
     public static Selector Open();
      得到一个选择器对象
     public int select(long timeout);
      监听所有注册通道,存在IO流操作是,会将对应的信息SelectionKey存入到内部的集
      合中,参数是一个超时时间
     public Set selectionKeys();
      返回当前Selector内部集合中保存的所有SelectionKey
    在这里插入图片描述
4.2 SelectionKey
  • SelectionKey
     表示Selector和网络通道直接的关系
     int OP_ACCEPT; 16 需要连接
     int OP_CONNECT; 8 已经连接
     int OP_READ; 1 读取操作
     int OP_WRITE; 4 写入操作
  • SelectionKey
     public abstract Selector selector();
      得到与之关联的 Selector 对象
     public abstract SelectableChannel channel();
      得到与之关联的通道
     public final Object attachment();
      得到与之关联的共享数据
     public abstract SelectionKey interestOps(int ops);
      设置或改变监听事件
     public final boolean isAcceptable();
      是否可以 accept
     public final boolean isReadable();
      是否可以读
     public final boolean isWritable();
      是否可以写
4.3 ServerSocketChannel
  • ServerSocketChannel
     服务端Socket程序对应的Channel通道
  • 常用方法:
     public static ServerSocketChannel open();
      开启服务器ServerSocketChannel通道,等于开始服务器程序
     public final ServerSocketChannel bind(SocketAddress local);
      设置服务器端端口号
     public final SelectableChannel configureBlocking(boolean block);
      设置阻塞或非阻塞模式, 取值 false 表示采用非阻塞模式
     public SocketChannel accept();
      [非阻塞]
        获取一个客户端连接,并且得到对应的操作通道
     public final SelectionKey register(Selector sel, int ops);
      [重点方法]
        注册当前选择器,并且选择监听什么事件
4.4 SocketChannel
  • SocketChannel
     客户端Socket对应的Channel对象

  • 常用方法:
     public static SocketChannel open();
      打卡一个Socket客户端Channel对象
     public final SelectableChannel configureBlocking(boolean block)
      这里可以设置是阻塞状态,还是非阻塞状态
      false,表示非阻塞
     public boolean connect(SocketAddress remote);
      连接服务器
     public boolean finishConnect();
      如果connect连接失败,可以通过finishConnect继续连接
     public int write(ByteBuffer buf);
      写入数据到缓冲流中
     public int read(ByteBuffer buf); 、
      从缓冲流中读取数据
     public final SelectionKey register(Selector sel, int ops, Object attechment);
      注册当前SocketChannel,选择对应的监听操作,并且可以带有Object attachment参数
      
     public final void close();
      关闭SocketChannel

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值