当前位置:   article > 正文

Java NIO 零拷贝 演示_java 零拷贝 demo

java 零拷贝 demo

一、传统IO演进(详细机制需要参考操作系统知识进行理解)

1.用户态与内核态,需要经过 4 次拷贝和 3 次切换

Data Memory Access 直接内存拷贝
KernelBuffer       内核缓冲区
CpuCopy            通过CPU拷贝
UserBuffer         用户缓冲区
SocketBuffer       Socket缓冲区
ProtocolEngine     协议栈
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
DMA
CpuCopy
CpuCopy
DMA
文件
KernelBuffer
UserBuffer
SocketBuffer
ProtocolEngine

2.内存映射优化(MMAP)需要经过 3 次拷贝和 3 次切换

用户空间共享内核区数据,减少 1 次CPU拷贝
  • 1
DMA
Shared
CpuCopy
DMA
文件
KernelBuffer
UserBuffer
SocketBuffer
ProtocolEngine

3.Linux2.1 SendFile 优化,需要经过 3 次拷贝和 2 次切换

数据不经过用户态,直接从内核缓冲区进入到Socket缓冲区
  • 1
DMA
CpuCopy
DMA
文件
KernelBuffer
SocketBuffer
ProtocolEngine

4.Linux2.4 内核修改,需要经过 2 次拷贝和 2 次切换

将2.1的从内核缓冲区到Socket缓冲区数据拷贝,修改为关键信息拷贝:length、offset 等
由于只拷贝文件描述信息,数据量很小,消耗低,可以忽略;相当于直接将数据拷贝到协议栈
  • 1
  • 2
DMA
CpuCopy DescriptionInfo
DMA
文件
KernelBuffer
SocketBuffer
ProtocolEngine

二、零拷贝演示

1.传统IO

1.服务端

package com.example.netty.io.zerocopy;

import java.io.DataInputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * Description: 传统IO服务端
 *
 * @Author: zhx & moon hongxu_1234@163.com
 * @Date: 2022-01-23 18:19
 * @version: V1.0.0
 */
public class OldCopyServer {

    public static void main(String[] args) throws IOException {

        ServerSocket serverSocket = new ServerSocket(7800);

        while (true){
            Socket socket = serverSocket.accept();
            DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
            byte[] buffer = new byte[4096];
            long readCount;
            long total = 0;
            while (-1!=(readCount = dataInputStream.read(buffer,0,buffer.length))){
                total += readCount;
            }
            //此处会体现为整字节 即最后Total为4096整倍数
            System.out.println("读取字节数: " + total);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

2.客户端

package com.example.netty.io.zerocopy;

import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;

/**
 * Description: 传统IO客户端
 *
 * @Author: zhx & moon hongxu_1234@163.com
 * @Date: 2022-01-23 18:07
 * @version: V1.0.0
 */
public class OldCopyClient {

    public static void main(String[] args) throws IOException {
        traditionalCopy("night.jpg");
    }

    /**
     * 发送数据
     * @param fileName
     * @throws IOException
     */
    public static void traditionalCopy(String fileName) throws IOException {

        //定义Socket
        Socket socket = new Socket("127.0.0.1",7800);
        //定义文件输入流
        InputStream inputStream = new FileInputStream(fileName);
        //定义数据输出流
        DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());
        //定义缓冲字节数组
        byte[] buffer = new byte[4096];
        //定义读取字节数和总字节数
        long readCount;
        long total = 0;
        //记录开始时间
        long start = System.currentTimeMillis();
        //循环读写
        while ((readCount = inputStream.read(buffer)) >= 0){
            total += readCount;
            dataOutputStream.write(buffer);
        }
        //打印结果
        System.out.println("发送总字节数:" + total + "  耗时(MS):" + (System.currentTimeMillis() - start));
        //关闭通道和流
        dataOutputStream.close();
        inputStream.close();
        socket.close();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

3.先启动服务,在启动客户端,查看结果

在这里插入图片描述

2.零拷贝IO

1.服务端

package com.example.netty.io.zerocopy;

import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

/**
 * Description: 零拷贝 服务端
 *
 * @Author: zhx & moon hongxu_1234@163.com
 * @Date: 2022-01-23 18:38
 * @version: V1.0.0
 */
public class NewCopyServer {

    public static void main(String[] args) throws IOException {

        //监听端口
        InetSocketAddress inetSocketAddress = new InetSocketAddress(7800);
        //创建ServerSocketChannel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //获取Socket
        ServerSocket serverSocket = serverSocketChannel.socket();
        //绑定
        serverSocket.bind(inetSocketAddress);
        //创建Buffer
        ByteBuffer buffer = ByteBuffer.allocate(4096);

        while (true){

            SocketChannel socketChannel = serverSocketChannel.accept();
            //创建输出流
            FileOutputStream fileOutputStream = new FileOutputStream("copy.jpg");
            //获取输出流通道
            FileChannel writeChannel = fileOutputStream.getChannel();
            long readCount;
            long total = 0;

            while (-1!=(readCount = socketChannel.read(buffer))){
                total += readCount;

                //切换状态
                buffer.flip();
                //写入文件
                writeChannel.write(buffer);
                //清空
                buffer.clear();
            }
            writeChannel.close();
            fileOutputStream.close();
            System.out.println("读取字节数:" + total);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

2.客户端

package com.example.netty.io.zerocopy;

import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;

/**
 * Description: 零拷贝 客户端
 *
 * @Author: zhx & moon hongxu_1234@163.com
 * @Date: 2022-01-23 18:51
 * @version: V1.0.0
 */
public class NewCopyClient {

    public static void main(String[] args) throws IOException {

        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.connect(new InetSocketAddress("127.0.0.1",7800));
        //1.创建一个输出流->channel
        FileInputStream fileInputStream = new FileInputStream("night.jpg");
        //2.获取输入流通道
        FileChannel readChannel = fileInputStream.getChannel();
        long start = System.currentTimeMillis();
        /**
         * transferTo 底层为零拷贝实现
         * 在linux   下一次调用 transferTo 方法就可以传输完成
         * 在windows 下一次调用 transferTo 只能发送 8mb 需要分段处理
         * 获取字节数 1mb = 1024kb = 1024 * 1024 byte = 1024 * 1024 * 1024 bit
         * 判断操作系统类型,如果为 windows 就获取字节数 fileInputStream.available()
         * 8mb = 1024 * 1024 byte 做循环发送即可
         */
        long transferCount = readChannel.transferTo(0,readChannel.size(),socketChannel);
        System.out.println("发送总字节数:" + transferCount + "  耗时(MS):" + (System.currentTimeMillis() - start));
        readChannel.close();
        socketChannel.close();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

3.先启动服务,在启动客户端,查看结果

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/753701
推荐阅读
相关标签
  

闽ICP备14008679号