Java图文并茂详解NIO与零拷贝

目录
  • 一、概念说明
    • 1、传统IO
    • 2、mmap
    • 3、sendfile
    • 4、mmap与sendfile
  • 二、传统IO传输文件代码示例
    • 1、服务端代码
    • 2、客户端代码
    • 3、控制台出输出
  • 三、NIO传输文件代码示例
    • 1、服务端代码
    • 2、客户端代码
    • 3、控制台出输出
  • 四、总结

零拷贝指的是没有CPU拷贝,并不是不拷贝;减少上下文切换

一、概念说明

1、传统IO

需要4次拷贝,3次上下文切换

2、mmap

mmap 通过内存映射,将文件映射到内存缓冲区,同时用户空间可以共享内存缓冲区的数据,减少内核空间到用户空间的拷贝

需要3次拷贝,3次上下文切换

3、sendfile

Linux 2.4 避免了从内核缓冲区到Socket Buffer的拷贝,直接拷贝到协议栈,从而减少一次数据拷贝

需要2次拷贝,3次上下文切换

4、mmap与sendfile

mmap适合小数据量读写,sendfile适合大文件传输

mmap需要4次上下文切换,3次数据拷贝;sendfile需要3次上下文切换,最少2次数据拷贝

send可用利用DMA方式,减少CPU拷贝,mmap则不能(必须从内核拷贝到Socket缓冲区)

二、传统IO传输文件代码示例

1、服务端代码

import java.io.DataInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class BIOServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(7000);
        while (true) {
            Socket socket = serverSocket.accept();
            DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
            try {
                long total = 0;
                byte[] bytes = new byte[4096];
                FileOutputStream fileOutputStream = new FileOutputStream("d:\\temp\\04.zip");
                while (true) {
                    int read = dataInputStream.read(bytes, 0, bytes.length);
                    if (read == -1) {
                        //文件读取结束,退出循环
                        break;
                    }
                    total += read;
                    fileOutputStream.write(bytes, 0, read);
                }
                System.out.println("收到客户端发送文件,总字节数:" + total);
                fileOutputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

2、客户端代码

import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.Socket;
public class BIOClient {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 7000);
        FileInputStream fileInputStream = new FileInputStream("d:\\temp\\03.zip");
        DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());
        byte[] bytes = new byte[4096];
        long readCount;
        long total = 0;
        long start = System.currentTimeMillis();
        while ((readCount = fileInputStream.read(bytes)) >= 0) {
            total += readCount;
            dataOutputStream.write(bytes);
        }
        System.out.println("发送总字节数:" + total + ", 总耗时:" + (System.currentTimeMillis() - start));
        dataOutputStream.close();
        socket.close();
        fileInputStream.close();
    }
}

3、控制台出输出

测试发送9M的压缩文件,耗时在26ms左右

发送总字节数:9428963, 总耗时:26

三、NIO传输文件代码示例

1、服务端代码

package com.hj.io.nio.zero;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
public class NIOServerFile {
    public static void main(String[] args) throws IOException {
        InetSocketAddress address = new InetSocketAddress(7000);
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(address);
        //创建buffer
        ByteBuffer byteBuffer = ByteBuffer.allocate(4096);
        while (true) {
            //等待客户端链接
            SocketChannel socketChannel = serverSocketChannel.accept();
            System.out.println("收到客户端链接");
            int total = 0;
            FileOutputStream fileOutputStream = new FileOutputStream("d:\\temp\\05.zip");
            //循环读取数据,并存储到硬盘
            while (true) {
                try {
                    int readCount = socketChannel.read(byteBuffer);
                    if (readCount == -1) {
                        //文件读取结束
                        break;
                    }
                    total += readCount;
                    fileOutputStream.write(byteBuffer.array(),0,readCount);
                    //将buffer倒带
                    byteBuffer.rewind();
                } catch (IOException e) {
                   break;
                }
            }
            System.out.println("收到客户端发送文件,总字节数:" + total);
            fileOutputStream.close();
        }
    }
}

2、客户端代码

import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;
public class NIOClientFile {
    public static void main(String[] args) throws IOException {
        //打开一个SocketChannel并链接到服务器端
        SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 7000));
        //打开一个文件
        FileChannel fileChannel = new FileInputStream("d:\\temp\\03.zip").getChannel();
        //发送文件到服务器
        long start = System.currentTimeMillis();
        //在linux下,一次transferTo调用就可以完成传输
        //在windows下,一次transferTo调用最多只能传8M,大文件需要分段传输,需要注意传输位置
        //transferTo底层使用零拷贝
        long total = fileChannel.size();
        long sended = 0;
        while (sended < total) {
            //从上一次传输位置继续发送
            long lenth = fileChannel.transferTo(sended, fileChannel.size(), socketChannel);
            sended += lenth;
        }
        System.out.println("发送总字节数:" + sended + ",总耗时:" + (System.currentTimeMillis() - start));
        //关闭channel
        fileChannel.close();
        socketChannel.close();
    }
}

3、控制台出输出

测试发送9M的压缩文件,耗时在16ms左右

发送总字节数:9428963,总耗时:16

四、总结

使用零拷贝传输,性能明显高于传统IO传输

到此这篇关于Java图文并茂详解NIO与零拷贝的文章就介绍到这了,更多相关Java NIO与零拷贝内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java使用NIO优化IO实现文件上传下载功能

    目录 1 NIO的一些基础预备知识 2 NIO为何较传统的io速度较快 3 NIO实战上传下载 3.1 url下载文件 3.2 通过NIO上传文件 1 NIO的一些基础预备知识 Java中IO流类的体系中BIO与NIO:https://blog.csdn.net/ZGL_cyy/article/details/104326458Java IO体系与NIO和BIO体系面试题 :https://blog.csdn.net/ZGL_cyy/article/details/122836368为什么使用N

  • Java NIO与IO的区别以及比较

    目录 前言 一.NIO的简介 二.IO的传统方式 三.NIO的详细介绍 四. 举例说明 前言 传统的socket IO中,需要为每个连接创建一个线程,当并发的连接数量非常巨大时,线程所占用的栈内存和CPU线程切换的开销将非常巨大.使用NIO,不再需要为每个线程创建单独的线程,可以用一个含有限数量线程的线程池,甚至一个线程来为任意数量的连接服务.由于线程数量小于连接数量,所以每个线程进行IO操作时就不能阻塞,如果阻塞的话,有些连接就得不到处理,NIO提供了这种非阻塞的能力. 小量的线程如何同时为大

  • Java BOI与NIO超详细实例精讲

    目录 Java BIO 示例代码 Java NIO 代码解读 Java BIO 阻塞IO,每个客户端链接都需要一个独立的线程处理,客户端链接没关闭时,线程链接处于阻塞状态,直到客户端链接关闭 如果客户端链接没有读取到数据,链接就会一直阻塞住,造成资源浪费 示例代码 开发一个ServerSocket服务端,通过telnet链接发送信息 import java.io.IOException; import java.io.InputStream; import java.net.ServerSock

  • Java NIO 中 Selector 解析

    目录 一.Selector 简介 1.Selector 和 Channel 关系 2.可选择通道(SelectableChannel) 3.Channel 注册到 Selector 4.选择键(SelectionKey) 二.Selector 的使用方法 1.Selector 的创建 2.注册 Channel 到 Selector 3.轮训查询就绪操作 4.停止选择的方法 三.示例代码 1.服务端代码 2.客户端代码 3.NIO 编程步骤总结 一.Selector 简介 1.Selector 和

  • Java NIO 中Buffer 缓冲区解析

    目录 一.Buffer 简介 二.Buffer 的基本方法 1.使用 Buffer 读写数据 2.使用 Buffer 的例子 三.Buffer 的 capactity.posittion 和limit 四.Buffer 的类型 五.Buffer 分配和写数据 1. Buffer 分配 2.向 buffer 中写数据 3.flip() 方法 六.从 Buffer 中读取数据 七.Buffer 几个方法 1.rewind() 方法 2.clear() 与 compact() 方法 3.mark()

  • Java中BIO、NIO和AIO的区别、原理与用法

    目录 IO BIO NIO AIO 区别及联系 各自适用场景 使用方式 IO 什么是IO? 它是指计算机与外部世界或者一个程序与计算机的其余部分的之间的接口.它对于任何计算机系统都非常关键,因而所有 I/O 的主体实际上是内置在操作系统中的.单独的程序一般是让系统为它们完成大部分的工作. 在 Java 编程中,直到最近一直使用 流 的方式完成 I/O.所有 I/O 都被视为单个的字节的移动,通过一个称为 Stream 的对象一次移动一个字节.流 I/O 用于与外部世界接触.它也在内部使用,用于将

  • Java NIO Channel 使用详情

    目录 FileChannel 输入 FileInputStream 输出 FileOutputStream RandomAccessFile 两个 SocketChannel ServerSocketChannel SocketChannel Datagram Java NIO 中的 Channel 分类: FileChannel SocketChannel ServerSocketChannel DatagramChannel FileChannel: 主要用于文件的读写,可以从磁盘上读取文件

  • JavaIO模型中的BIO,NIO和AIO详解

    目录 一.I/O模型 1.1 I/O模型基本说明 1.2 Java支持的3种网络编程I/O模式 1.3 JavaBIO(同步阻塞) 1.4 JavaNIO (同步非阻塞) 1.5 JavaAIO(异步非阻塞) 二.BIO.NIO.AIO适用场景分析 2.1 BIO应用场景 2.2 NIO应用场景 2.3 AIO应用场景 总结 一.I/O模型 1.1 I/O模型基本说明 I/O模型的简单理解:I/O模型就是用什么样的通道进行数据的发送和接受,很大程度上决定了程序通信的性能 1.2 Java支持的3

  • Java图文并茂详解NIO与零拷贝

    目录 一.概念说明 1.传统IO 2.mmap 3.sendfile 4.mmap与sendfile 二.传统IO传输文件代码示例 1.服务端代码 2.客户端代码 3.控制台出输出 三.NIO传输文件代码示例 1.服务端代码 2.客户端代码 3.控制台出输出 四.总结 零拷贝指的是没有CPU拷贝,并不是不拷贝:减少上下文切换 一.概念说明 1.传统IO 需要4次拷贝,3次上下文切换 2.mmap mmap 通过内存映射,将文件映射到内存缓冲区,同时用户空间可以共享内存缓冲区的数据,减少内核空间到

  • Java BigDecimal详解_动力节点Java学院整理

    1.引言 借用<Effactive Java>这本书中的话,float和double类型的主要设计目标是为了科学计算和工程计算.他们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的.然而,它们没有提供完全精确的结果,所以不应该被用于要求精确结果的场合.但是,商业计算往往要求结果精确,例如银行存款数额,这时候BigDecimal就派上大用场啦. 2.BigDecimal简介 BigDecimal 由任意精度的整数非标度值 和32 位的整数标度 (scale) 组

  • Java 基础详解(泛型、集合、IO、反射)

    计划把 Java 基础的有些部分再次看一遍,巩固一下,下面以及以后就会分享自己再次学习的一点笔记!不是有关标题的所有知识点,只是自己觉得模糊的一些知识点. 1.对于泛型类而言,你若没有指明其类型,默认为Object: 2.在继承泛型类以及接口的时候可以指明泛型的类型,也可以不指明: 3.泛型也数据库中的应用: 写一个 DAO 类对数据库中的数据进行增删改查其类型声明为 <T> .每张表对应一个类,对应每一张表实现一个类继承该 DAO 类并指明 DAO 泛型为该数据表对应的类,再实现一个与该表匹

  • 浅析Java虚拟机详解之概述、对象生存法则

    Java与C++之间有一堵由内存分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 一.概述 Java堆和方法区这两个区域有着很显著的不确定性: 1.一个接口的多个实现类需要的内存可能会不一样,一个方法所执行的不同条件分支所需要的内存也可能不一样 2.只有处于运行期间,我们才能知道程序究竟会创建哪些对象,创建多少个对象,这部分内存的分配和回收是动态的 垃圾收集器所关注的正是这部分的内存该如何管理 二.对象已死? 1.引用计数法 在对象中添加一个引用计数器,每当有一个地方引用它

  • SpringBoot图文并茂详解如何引入mybatis与连接Mysql数据库

    目录 创建一个SpringBoot项目 创建mysql表 编写实体类 配置Mapper 感叹 创建一个SpringBoot项目 其他不赘叙了,引入MyBaties.MySql依赖 创建mysql表 CREATE TABLE sp_users( `id` INT PRIMARY KEY, `username` VARCHAR(30), `age` INT ); 刚开始一直出现这个错误,弄的我怀疑人生,结果是最后一行不能加',' ,物是人非. INSERT INTO sp_users(id,`use

  • Java递归算法详解(动力节点整理)

    递归算法是一种直接或者间接调用自身函数或者方法的算法.Java递归算法是基于Java语言实现的递归算法.递归算法的实质是把问题分解成规模缩小的同类问题的子问题,然后递归调用方法来表示问题的解.递归算法对解决一大类问题很有效,它可以使算法简洁和易于理解.    递归算法解决问题的特点: 1)递归就是方法里调用自身.  2) 在使用递增归策略时,必须有一个明确的递归结束条件,称为递归出口.  3)递归算法解题通常显得很简洁,但递归算法解题的运行效率较低.所以一般不提倡用递归算法设计程序.  4)在递

  • Java 匿名内部类详解及实例代码

    Java  匿名内部类详解 匿名内部类也就是没有名字的内部类 正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写 但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口 实例1:不使用匿名内部类来实现抽象方法 abstract class Person { public abstract void eat(); } class Child extends Person { public void eat() { System.out.println("eat someth

  • Java设计模式详解之门面模式(外观模式)

    门面模式(Facade Pattern)也叫外观模式,它隐藏系统的复杂性,并向客户端提供一个可以访问系统的接口.这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性,为子系统中的一组接口提供了一个统一的高层访问接口,这个接口使得子系统更容易被访问或使用.这种模式涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用. 简而言之,就是把一堆复杂的流程封装成一个接口供给用户更简单的使用,这个设计模式里有三个角色: 1)门面角色( facade ):

  • Java Cache详解及简单实现

     Java Cache详解及简单实现 概要: 最近在做spring的项目,想做一个缓存,访问数据库,定期来做数据更新 要实现两个功能 可以通过http请求来立刻刷新缓存 缓存可以通过自己配置的时间间隔来定期刷新 通过Controller来做 因为需要通过http来刷新缓存,所以第一个想法就是把缓存做成一个Controller Controller的实现 Controller最大的优势,就是可以通过Spring的配置,注入很多依赖,比如对Service的依赖,对数据库的依赖等. 大量的访问数据库跟

  • java 二分法详解几种实现方法

    java 二分法详解几种方法 二分查找(java实现) 二分查找 算法思想:又叫折半查找,要求待查找的序列有序.每次取中间位置的值与待查关键字比较,如果中间位置的值比待查关键字大,则在前半部分循环这个查找的过程,如果中间位置的值比待查关键字小,则在后半部分循环这个查找的过程.直到查找到了为止,否则序列中没有待查的关键字. 实现: 1.非递归代码 public static int biSearch(int []array,int a){ int lo=0; int hi=array.length

随机推荐