7、深究NIO中的零拷贝

一、零拷贝概述

零拷贝说起来似乎是一个很高大上的东西,但是理解了之后也就那么会事,挺简单的。了解NIO中的零拷贝之前需要知道计算机在运行的过程中是分为两种状态的——用户态、核心态。这两个概念是操作系统的范畴,他们之间的切换越少越好因为会带来上下文切换的开销。还有一个就是这里说的零拷贝并不是真正意义上的一次拷贝操作都没有而是指不会发生用户态到内核态的之间的拷贝。下图中传统模式的标记为红色的两种拷贝就是这样的拷贝所以说他是很浪费资源的。有关此流程图中的线上链接为:https://www.processon.com/view/link/62724baa5653bb5be573ffd8

网上有很多博客参考的图片应该都是一本书的但是按个对于新手感觉不是很友好,这个就根据自己的认识与理解重新画了一下,此处的场景是进行网络通信,当然也可能是修改文件后就直接保存呢。

此文章参考的核心博客为:(4条消息) Java NIO学习笔记四(零拷贝详解)_拿笔小星Z的博客-CSDN博客_nio 零拷贝
7、深究NIO中的零拷贝_第1张图片
前两种的NIO模式并不能够进行文件的修改他使用的场景是直接将文件发送给目标机器,第三种的mmap的实现可以进行文件修改。同时应该明白的是零拷贝依靠的是操作系统的支持与语言的编写并无关系。如果系统不支持相关零拷贝的实现那么语言也是无论如何实现不了的。

二、零拷贝的实现

零拷贝实际上也并没有什么稀奇之处,原来读取数据的时候我们可能会先将数据写入到一个byte的数组中,在零拷贝中我们使用ByteBuffer来替代byte数组。

1、传统方式的实现

读写的过程中因为byte数组的大小会印象传输的速度同时,因为不能够精确的掌控Byte数组的使用情况所以当最后一次的使用不满的时候那就会出现偏差。

//java IO 的服务器
public class OldIOServer {
    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(7001);
        long total = 0;

        //外面的这一层循环主要是为了等待一个连接进来,实际的场景中应该将具体的任务交给一个线程去执行,从而避免过度的阻塞
        while (true) {
            Socket socket = serverSocket.accept();
            DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
            try {
                byte[] byteArray = new byte[2048];
                while (true) {
                    //这里读取到的是一个
                    int readCount = dataInputStream.read(byteArray, 0, byteArray.length);
                    if (-1 == readCount) {
                        break;
                    }
                    total += readCount;
                }
                System.out.println("共计接受:"+total);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }
}

public class OldIOClient {
    public static void main(String[] args) throws Exception {
        Socket socket = new Socket("localhost", 7001);
        String fileName = "protoc-3.6.1-win32.zip";
        InputStream inputStream = new FileInputStream(fileName);
        //无关数据平台的输出流,通过他架起了网络套接字的输出流,从而工本地操作
        DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());
        //创建一个用来暂放数据的空间,相当于是用户空间的缓存
        byte[] buffer = new byte[2048];
        long readCount;
        long total = 0;

        long startTime = System.currentTimeMillis();
        //此条件表示目前尚有数据,但是有一个问题就是buffer中并没清除掉原有的数据,这样的写应该是会存在写的情况
        //不过计数会是正确的因为InputStream的read方法使用正确
        while ((readCount = inputStream.read(buffer)) >= 0) {
            total += readCount;
            dataOutputStream.write(buffer);
        }

        System.out.println("发送总字节数: " + total + ", 耗时: " + (System.currentTimeMillis() - startTime));
        dataOutputStream.close();
        socket.close();
        inputStream.close();
    }
}

2、零拷贝的方式

public class NewIOClient {
    public static void main(String[] args) throws Exception {
        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.connect(new InetSocketAddress("localhost", 7001));
        String filename = "protoc-3.6.1-win32.zip";
        //得到一个文件channel
        FileChannel fileChannel = new FileInputStream(filename).getChannel();
        //准备发送
        long startTime = System.currentTimeMillis();
        //transferTo 底层使用到零拷贝
        long transferCount = fileChannel.transferTo(0, fileChannel.size(), socketChannel);
        System.out.println("发送的总的字节数 =" + transferCount + " 耗时:" + (System.currentTimeMillis() - startTime));
        //关闭
        fileChannel.close();
    }
}

public class NewIOServer {
    public static void main(String[] args) throws Exception {
        InetSocketAddress address = new InetSocketAddress(7001);
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        ServerSocket serverSocket = serverSocketChannel.socket();
        serverSocket.bind(address);
        //创建buffer
        ByteBuffer byteBuffer = ByteBuffer.allocate(4096);
        while (true) {
            SocketChannel socketChannel = serverSocketChannel.accept();
            int readcount = 0;
            while (-1 != readcount) {
                try {
                    readcount = socketChannel.read(byteBuffer);
                }catch (Exception ex) {
                   // ex.printStackTrace();
                    break;
                }
                byteBuffer.rewind(); //倒带 position = 0 mark 作废
            }
        }
    }
}

                }
                byteBuffer.rewind(); //倒带 position = 0 mark 作废
            }
        }
    }
}

你可能感兴趣的:(行无止境,NIO零拷贝,零拷贝原理,零拷贝实现对照)