Netty-NIO 零拷贝

描述:

在java程序中,常用的零拷贝有mmap(内存映射) 和 sendFile,那么他们在系统里,到底是怎么样的一个设计?

在nio中是如何实现零拷贝的呢?

传统io拷贝

直接内存拷贝:
在这里插入图片描述

从图中可以看到,内核缓冲区,拷贝到 用户缓冲,在拷贝到socket 缓冲,在dma 拷贝,这是非常耗费资源的。

mmap优化

在这里插入图片描述

mmap 通过内存映射,将文件映射到内核缓冲区中,同时用户空间可以共享内核空间的数据,这样在网络传输的时候,就可以减少内核空间到用户空间的拷贝。

sendFile 1.0

在这里插入图片描述

数据传输不经过用户态,直接从内核缓冲区 进入 到 socket buffer,这样就少一次 用户态的上下文切换。

sendFile 2.0

在这里插入图片描述

sendFile 再次升级,内核缓冲区,直接不经过 socket buffer 直接和协议栈进行交互,这样就再一次减少了拷贝,

注意: 这里内核缓冲区,会拷贝一些少量的数据到 socket buffer区,但是这些数据基本可以忽略不计。

代码实例:

传统io 网络传输实例:

/**
 * @Author qrn
 * @Title
 * @Date 2021/5/14 10:06
 * @time 10:06
 * java io 拷贝 服务端:
 */
public class BioServer {

    public static void main(String[] args) throws  Exception  {
        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){
                //读取数据 等于 -1的时候 就表示数据读取完,就跳出循环
                int read = dataInputStream.read(byteArray, 0, byteArray.length);
                if(-1 == read){
                    break;
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        }
    }

}
/**
 * @Author qrn
 * @Title
 * @Date 2021/5/14 19:07
 * @time 19:07
 * java io 客户端 ,发送一个 zip包到服务端,传输查看花费的时间。
 */
public class BioClient {

    public static void main(String[] args) throws  Exception{
        Socket socket = new Socket("127.0.0.1",7001);
        String fileName = "protoc-3.6.1-win32.rar";
        //输入流
        FileInputStream fileInputStream = new FileInputStream(fileName);
        //输出流,发送文件给服务端:
        DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());

        byte[] buffer = new byte[4096];
        long readcout;
        long total = 0;
        long startTime = System.currentTimeMillis();
        while ((readcout = fileInputStream.read(buffer)) >= 0){
            total += readcout;
            dataOutputStream.write(buffer);
        }

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

    }
}

nio 零拷贝

/**
 * @Author qrn
 * @Title
 * @Date 2021/5/14 19:27
 * @time 19:27
 * nio 服务端,零拷贝实例:
 */
public class NioServer {

    public static void main(String[] args) throws  Exception {
        InetSocketAddress address = new InetSocketAddress(7001);
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        ServerSocket serverSocket = serverSocketChannel.socket();
        serverSocket.bind(address);

        ByteBuffer allocate = ByteBuffer.allocate(4096);
        while (true){

            SocketChannel socketChannel = serverSocketChannel.accept();
            int readcount = 0;
            while (-1 != readcount){
                try{
                    readcount = socketChannel.read(allocate);
                }catch (Exception e) {
                   break;
                }
                allocate.rewind();// 倒带
            }

        }

    }
}

/**
 * @Author qrn
 * @Title
 * @Date 2021/5/14 19:38
 * @time 19:38
 * nio 零拷贝 客户端:
 */
public class NioCllient {
    public static void main(String[] args) throws  Exception{
        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.connect(new InetSocketAddress("127.0.0.1", 7001));
        String fileName = "protoc-3.6.1-win32.rar";
        FileChannel fileChannel = new FileInputStream(fileName).getChannel();
        long startTime = System.currentTimeMillis();
        /**
         * transferTo  在linux 下可以完成全部传输,在Windows 下一次只能传 8 m
         */

        long transferCount = 0;
        //如果是windows 就需要循环
        if(System.getProperty("os.name").toLowerCase().contains("win")){
            transferCount = testFileChannel(fileChannel,socketChannel,transferCount);
        }else{
            transferCount += fileChannel.transferTo(0, fileChannel.size(), socketChannel);
        }

        System.out.println("发送的总的字节数 =" + transferCount + " 耗时:" + (System.currentTimeMillis() - startTime));
        //关闭
        fileChannel.close();
    }

    public static  Long testFileChannel(FileChannel fileChannel,SocketChannel socketChannel,long transferCount) throws Exception {
        Integer num = 8*1024*1024;
        Long fiel = fileChannel.size();
        while (true){
            if(fiel <= num){
                transferCount += fileChannel.transferTo(0, fiel, socketChannel);
                break;
            }
            transferCount += fileChannel.transferTo(0, num, socketChannel);
            fiel = fiel - num;
        }
        return  transferCount;
    }
}

代码实例放在github上面了,需要的可以自己去上面看 : Nrtty-Notes

如果这篇文章,有帮助到大家的,请给作者一个一键三连,谢谢。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值