零拷贝

NIO与零拷贝

零拷贝基本介绍

  1. 零拷贝是网络编程 的关键,很多性能优化都离不开
  2. 在java 程序中,常用的零拷贝有mmap(内存映射) 和 sendFile,那么,他们在OS李,到底是怎样一个设计? 我们分析mmap和sendFile这两个零拷贝
  3. 另外我们看一下NIO中如何使用零拷贝

传统io数据读写

  1. java传统IO和网络编程的一段代码
        File file = new File("test.txt");
        RandomAccessFile raf = new RandomAccessFile(file, "rw");
        byte[] arr = new byte[(int) file.length()];
        raf.read(arr);
        Socket socket = new ServerSocket(8080).accept();
        socket.getOutputStream().write(arr);

mmap优化

  1. mmap通过内存映射,将文件映射到内核缓冲区,同时,用户空间可以共享内核空间的数据,这样, 在进行网络传输时, 就可以减少内核空间到用户空间的拷贝次数,如下图
    在这里插入图片描述

sendFile优化

  1. linux2.1版本提供了sendFile函数,其基本原理如下: 数据根本不经过用户态,直接从内核缓冲区进入到socketBuffer, 同时, 由于和用户态完全无关,就减少了一次上下文切换
    在这里插入图片描述
  2. linux在 2.4 版本中,做了一些修改,避免从内核缓冲区拷贝到SocketBuffer的操作,直接拷贝到协议栈,从而再次减少了数据拷贝,具体如下图和小结在这里插入图片描述这里其实有一次cpu 拷贝,
    kernel buffer -> socket buffer
    但是,拷贝信息很少,比如length,offset 信息很少消耗低,可以忽略, 实际是 2两次拷贝

使用传统方式进行数据拷贝代码实现

Socket socket = new Socket("localhost", 7001);
        String fileName = "protoc.3.6.1-win32.zip";
        FileInputStream fileInputStream = new FileInputStream(fileName);
        DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());
        byte[] buffer = new byte[4096];
        long readCount;
        long total = 0;
        long startTime = System.currentTimeMillis();
        while ((readCount = fileInputStream.read(buffer)) >= 0) {
            total += readCount;
            dataOutputStream.write(buffer);
        }
        System.out.println("发送字节数: " + total + "耗时: "+ (System.currentTimeMillis() - startTime));
        dataOutputStream.close();
        socket.close();
        fileInputStream.close();

服务端代码

ServerSocket serverSocket = new ServerSocket(7001);
        while (true) {
            Socket socket = serverSocket.accept();
            DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
            try {
                byte[] byteArray = new byte[4096];
                while (true) {
                    int readCount = dataInputStream.read(byteArray, 0, byteArray.length);
                    if (-1 == readCount) {
                        break;
                    }
                }
            } catch (Exception exception) {
                exception.printStackTrace();
            }
        }

使用零拷贝方式进行数据传输
客户端代码

		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();
        // 在linux下一个transferTo 方法就可以完成传输
        // 在windows下 一次调用transferTo只能发送8M文件,
        // 就需要分段传输文件, 而且要注意传输时的位置
        long transferCount = fileChannel.transferTo(0, fileChannel.size(), socketChannel);
        System.out.println("发送总的字节数= " + transferCount + "耗时: " + (System.currentTimeMillis() - startTime));
        fileChannel.close();
    

服务端

		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 e) {
                    break;
                }
                // 将buffer倒带
                byteBuffer.rewind(); // position 编程0 , mark作废
            }
        }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值