NIO、BIO

1.BIO概述

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

2.NIO概述

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

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 操作文件数据

package com.qfedu.b_niofile;

import org.junit.Test;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
 * @author Anonymous 2020/3/13 15:17
 */
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<SelectionKey> 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
发布了27 篇原创文章 · 获赞 45 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_41986648/article/details/104878201