NIO

275 阅读5分钟

一、I/O基础

输入流:InputStream和Reader 输出流:OutputStream和Writer

​ 字节流 字符流

计算机最小的二进制单位 bit 比特 代表0和1 字节 1 byte = 8bit 计算机处理的最小单位 字符 1 char = 2byte = 16bit 人处理的最小单位

所以,字节流处理文件、图片、视频等二进制数据,而字符流处理文本数据。

下面写一个示例

import java.io.*;

public class BioTest {
    public static void main(String[] args) throws IOException {
        copy("io.txt","io1.txt");

    }
    //读取数据
    public static void read() throws IOException {
        File file = new File("io.txt");
        if(!file.exists()){
            file.createNewFile();
        }
        //读取字节数据
        FileInputStream inputStream = new FileInputStream(file);
        FileReader fileReader = new FileReader(file);//使用字符流
        char[] chars = new char[(int)file.length()];//使用一个char数组类承接内容
        int read1 = fileReader.read(chars);
        System.out.println("读取字节的大小"+read1+"读取的内容"+new String(chars));
        byte[] byteArr = new byte[(int)file.length()];//使用一个byte数组来承接内容
        int read = inputStream.read(byteArr);
        System.out.println("读取字节的大小"+read+"读取的内容"+new String(byteArr));
        fileReader.close();
        inputStream.close();
    }
    //写入数据
    public static void write() throws IOException {
        File file = new File("io.txt");
        if(!file.exists()){
            file.createNewFile();
        }
        //写入字节数据
        String str = "--hello bio";
        FileOutputStream fileOutputStream = new FileOutputStream(file,true);
        fileOutputStream.write(str.getBytes());
        fileOutputStream.flush();;
        fileOutputStream.close();
    }

    public static void copy(String src,String dest) throws IOException {
        File srcFile = new File(src);
        File destFile = new File(dest);
        if(!destFile.exists()){
            destFile.createNewFile();
        }
        FileInputStream inputStream = new FileInputStream(srcFile);
        FileOutputStream fileOutputStream = new FileOutputStream(destFile);
        byte[] bytes = new byte[1024];
        int read = 0;
        //当读取到文件尾退出循环
        while((read = inputStream.read(bytes)) != -1){
            fileOutputStream.write(bytes,0,read);
        }
        fileOutputStream.flush();
        inputStream.close();
        fileOutputStream.close();
    }

    public static void copy1(String src,String dest) throws IOException {
        File srcFile = new File(src);
        File destFile = new File(dest);
        if(!destFile.exists()){
            destFile.createNewFile();
        }
        FileInputStream inputStream = new FileInputStream(srcFile);
        BufferedInputStream bis = new BufferedInputStream(inputStream);
        FileOutputStream fileOutputStream = new FileOutputStream(destFile);
        BufferedOutputStream bos = new BufferedOutputStream(fileOutputStream);
        int read = 0;
        //当读取到文件尾退出循环
        while((read = bis.read()) != -1){
            bos.write(read);
        }
        fileOutputStream.flush();
        inputStream.close();
        fileOutputStream.close();
    }
}

二、Socket

原意是“插座”,在计算机领域中,翻译为“套接字”。 本质上,是计算机之间进行通信的一种方式。

Linux,“一切皆文件”,给每个文件映射一个ID,叫做"文件描述符"。 当处理网络连接时,也会看成一个文件,read/write变成和远程计算机的交互。

OSI七层模型 = Open System Interconnection 开放式系统互联 从下到上分别为:物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。

实际应用的是优化后的TCP/IP模型(四层) 网络接口层/链路层、网络层、传输层、应用层

应用层协议:HTTP、FTP、SMTP(邮件协议) 传输层协议:TCP、UDP

Socket其实是应用层与传输层之间的抽象层,是一组接口。 在设计模式中,是门面模式。

3、NIO

BIO - BlockingIO 同步阻塞 NIO - New IO / Non-Blocking IO 同步非阻塞 AIO - Asynchronous IO 异步非阻塞

同步和异步,关注的是消息通知的机制 阻塞和非阻塞,关注的是等待消息过程中的状态

多路复用的模型

三大元素:Channel 、Buffer、Selector

1) Channel

FileChannel 文件管道的数据 Pipe.SinkChannel Pipe.SourceChannel 线程间通信的管道 ServerSocketChannel SocketChannel 用于TCP网络通信的管道 DatagramChannel 用于UDP网络通信的管道

2) Buffer

capacity 总体容量大小 limit 存储容量的大小,是可读写和不可读写的界线 position 已读容量的大小,已读和未读区域的界线

【使用原理】

a) 初始化,给定总容量,position=0, limit=capacity

b) 当使用put方法存入数据是,通过position来记录存储的容量变化,position不断后移,直到存储结束(写完成)

c)写完成需要调用flip方法刷新,limit=position,position=0 保障limit记录的是可读写区域的大小,position已读部分重置为空

d) 读数据直到读完成,需要调用clear方法,position=0, limit=capacity

3) Selector

三个元素: Selector选择器、SelectableChannel可选择的通道、SelectionKey选择键

本质上,Selector是监听器,监听的是通道是否有我们关心的操作产生,操作对应的是事件(连接、接收、读/写),使用SelectionKey代表具体的事件,在确保通道是可选择的情况下,将通道注册进选择器中,此时Selector维护的是,通道和事件之间的关联关系。 Selector,管理被注册的通道集合,以及他们的状态 SelectableChannel,是一个抽象类,提供了通道可被选择需要实现的api。 FileChannel就不是可选择的,Socket相关的通道都是可选择的 一个通道可以被注册到多个选择器上吗? 可以的 多个通道可以注册到一个选择器上,但一个通道只能在一个选择器中注册一次

SelectionKey,封装了要监听的事件,连接、接收、读、写。 一方面,Selector关心通道要处理哪些事件 另一方面,当事件触发时,通道要处理哪些事件

【使用方式】

a、首先通过open方法,获取通道,将通道设置为非阻塞的

b、通过open方法,获取选择器,将通道注册进选择器中,伴随设置通道要处理的事件(OP_ACCEPT)

c、轮询选择器,当前是否有要处理的操作 select() > 0? 如果有,要获取待处理操作的集合Set<SelectionKey> , 进行遍历 遍历到SelectionKey时,判断对应哪种操作,不同的操作设置不同的处理方式 如OP_ACCEPT,接收客户端通道并进行注册,监听后续处理的事件,如OP_WRITE 如OP_WRITE,通过key的方法获取通道本身,读取数据并继续监听事件,如OP_READ

4、零拷贝

需求:将磁盘中的文件读取出来,通过socket发送出去

传统的拷贝方式(4次) Socket网络缓冲区,也属于操作系统的内核缓冲区。 在操作系统中进行的拷贝(如第二次和第三次),叫做CPU拷贝。 连接磁盘或网卡等硬件的拷贝(如第一次和第四次),叫做DMA拷贝。

零拷贝的概念:减少CPU拷贝的次数。

零拷贝是基于操作系统层面的优化方式(以下基于Linux系统)

1) mmap = memory mapping 内存映射

2)sendfile (linux2.1内核支持) 3) sendfile with scatter/gather copy(批量sendfile) 从单个文件的处理,上升到多个物理地址的处理,提高处理速度

4)splice (拼接,在linux2.6内核支持)

在操作系统内核缓冲区和Socket网络缓冲区之间建立管道,来减少拷贝次数。