零拷贝案例 ( transferTo | transferFrom )

本文通过对比NIO的零拷贝与BIO传统拷贝在传输20MB文件时的时间消耗,展示了零拷贝技术显著提高的传输效率,服务器端和客户端实现代码详析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

https://blog.csdn.net/shulianghan/article/details/106447485

 




 

一、 案例需求


给出两个案例 , 一个是 使用普通的 BIO 模型 传输文件的案例 , 一个是 NIO + 零拷贝 传输文件案例 ;

传输 20M 的文件 , 对比二者的传输效率 ;




 

二、 传统 BIO 拷贝案例


服务器端使用 ServerSocket , 客户端使用 Socket , 在客户端将文件传输给服务器端 , 并统计整体的时间消耗 ;

1 . 服务器端代码 : 服务器端程序启动后 , 监听 8888 端口 , 等待客户端连接 , 客户端连接成功后 , 读取客户端上传的数据 , 服务器端将接收到的数据存储在 book2.pdf 文件中 ;

<span style="color:#000000"><span style="background-color:#2d2d2d"><code class="language-java"><span style="color:#cc99cc">package</span> kim<span style="color:#999999">.</span>hsl<span style="color:#999999">.</span>nio<span style="color:#999999">.</span>zerocopy<span style="color:#999999">;</span>

<span style="color:#cc99cc">import</span> java<span style="color:#999999">.</span>io<span style="color:#999999">.</span>FileOutputStream<span style="color:#999999">;</span>
<span style="color:#cc99cc">import</span> java<span style="color:#999999">.</span>io<span style="color:#999999">.</span>IOException<span style="color:#999999">;</span>
<span style="color:#cc99cc">import</span> java<span style="color:#999999">.</span>io<span style="color:#999999">.</span>InputStream<span style="color:#999999">;</span>
<span style="color:#cc99cc">import</span> java<span style="color:#999999">.</span>net<span style="color:#999999">.</span>ServerSocket<span style="color:#999999">;</span>
<span style="color:#cc99cc">import</span> java<span style="color:#999999">.</span>net<span style="color:#999999">.</span>Socket<span style="color:#999999">;</span>

<span style="color:#cc99cc">public</span> <span style="color:#cc99cc">class</span> BIOFileServerDemo <span style="color:#999999">{</span>
    <span style="color:#cc99cc">public</span> <span style="color:#cc99cc">static</span> <span style="color:#cc99cc">void</span> <span style="color:#6699cc">main</span><span style="color:#999999">(</span>String<span style="color:#999999">[</span><span style="color:#999999">]</span> args<span style="color:#999999">)</span> <span style="color:#999999">{</span>
        <span style="color:#cc99cc">try</span> <span style="color:#999999">{</span>
            <span style="color:#999999">// 1. 创建服务器套接字, 并等待客户端连接</span>
            ServerSocket serverSocket <span style="color:#99cc99">=</span> <span style="color:#cc99cc">new</span> ServerSocket<span style="color:#999999">(</span><span style="color:#f99157">8888</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
            System<span style="color:#999999">.</span>out<span style="color:#999999">.</span><span style="color:#6699cc">println</span><span style="color:#999999">(</span><span style="color:#99cc99">"服务器启动,监听 8888 端口"</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
            <span style="color:#999999">//阻塞, 等待客户端连接请求 ( 此处是第一个阻塞点 )</span>
            Socket socket <span style="color:#99cc99">=</span> serverSocket<span style="color:#999999">.</span><span style="color:#6699cc">accept</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
            <span style="color:#cc99cc">long</span> startTime <span style="color:#99cc99">=</span> System<span style="color:#999999">.</span><span style="color:#6699cc">currentTimeMillis</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
            System<span style="color:#999999">.</span>out<span style="color:#999999">.</span><span style="color:#6699cc">println</span><span style="color:#999999">(</span><span style="color:#99cc99">"客户端连接成功"</span><span style="color:#999999">)</span><span style="color:#999999">;</span>

            <span style="color:#999999">// 2. 接收客户端传输的数据, 并写出到文件中</span>
            InputStream inputStream <span style="color:#99cc99">=</span> socket<span style="color:#999999">.</span><span style="color:#6699cc">getInputStream</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
            FileOutputStream fileOutputStream <span style="color:#99cc99">=</span> <span style="color:#cc99cc">new</span> FileOutputStream<span style="color:#999999">(</span><span style="color:#99cc99">"book2.pdf"</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
            <span style="color:#cc99cc">byte</span><span style="color:#999999">[</span><span style="color:#999999">]</span> buffer <span style="color:#99cc99">=</span> <span style="color:#cc99cc">new</span> byte<span style="color:#999999">[</span><span style="color:#f99157">4096</span><span style="color:#999999">]</span><span style="color:#999999">;</span>

            <span style="color:#cc99cc">int</span> readLen<span style="color:#999999">;</span>
            <span style="color:#999999">// 读取的字节个数大于等于 0 才写出数据</span>
            <span style="color:#cc99cc">while</span> <span style="color:#999999">(</span> <span style="color:#999999">(</span> readLen <span style="color:#99cc99">=</span> inputStream<span style="color:#999999">.</span><span style="color:#6699cc">read</span><span style="color:#999999">(</span>buffer<span style="color:#999999">)</span> <span style="color:#999999">)</span> <span style="color:#99cc99">>=</span> <span style="color:#f99157">0</span> <span style="color:#999999">)</span> <span style="color:#999999">{</span>
                <span style="color:#999999">// 写出数据到服务器</span>
                fileOutputStream<span style="color:#999999">.</span><span style="color:#6699cc">write</span><span style="color:#999999">(</span>buffer<span style="color:#999999">,</span> <span style="color:#f99157">0</span><span style="color:#999999">,</span> readLen<span style="color:#999999">)</span><span style="color:#999999">;</span>
            <span style="color:#999999">}</span>
            System<span style="color:#999999">.</span>out<span style="color:#999999">.</span><span style="color:#6699cc">println</span><span style="color:#999999">(</span><span style="color:#99cc99">"文件传输完毕, 用时 : "</span> <span style="color:#99cc99">+</span> <span style="color:#999999">(</span>System<span style="color:#999999">.</span><span style="color:#6699cc">currentTimeMillis</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#99cc99">-</span> startTime<span style="color:#999999">)</span> <span style="color:#99cc99">+</span> <span style="color:#99cc99">" ms"</span><span style="color:#999999">)</span><span style="color:#999999">;</span>

            <span style="color:#999999">// 3. 关闭流</span>
            socket<span style="color:#999999">.</span><span style="color:#6699cc">close</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
            inputStream<span style="color:#999999">.</span><span style="color:#6699cc">close</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
            fileOutputStream<span style="color:#999999">.</span><span style="color:#6699cc">close</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
        <span style="color:#999999">}</span> <span style="color:#cc99cc">catch</span> <span style="color:#999999">(</span>IOException e<span style="color:#999999">)</span> <span style="color:#999999">{</span>
            e<span style="color:#999999">.</span><span style="color:#6699cc">printStackTrace</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
        <span style="color:#999999">}</span>
    <span style="color:#999999">}</span>
<span style="color:#999999">}</span>
</code></span></span>
  • 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

2 . 客户端代码 : 客户端连接本地的 8888 端口服务器 , 读取本地的 book.pdf 文件 , 将其传输到服务器中 ;

<span style="color:#000000"><span style="background-color:#2d2d2d"><code class="language-java"><span style="color:#cc99cc">package</span> kim<span style="color:#999999">.</span>hsl<span style="color:#999999">.</span>nio<span style="color:#999999">.</span>zerocopy<span style="color:#999999">;</span>

<span style="color:#cc99cc">import</span> java<span style="color:#999999">.</span>io<span style="color:#999999">.</span>FileInputStream<span style="color:#999999">;</span>
<span style="color:#cc99cc">import</span> java<span style="color:#999999">.</span>io<span style="color:#999999">.</span>IOException<span style="color:#999999">;</span>
<span style="color:#cc99cc">import</span> java<span style="color:#999999">.</span>net<span style="color:#999999">.</span>Inet4Address<span style="color:#999999">;</span>
<span style="color:#cc99cc">import</span> java<span style="color:#999999">.</span>net<span style="color:#999999">.</span>InetSocketAddress<span style="color:#999999">;</span>
<span style="color:#cc99cc">import</span> java<span style="color:#999999">.</span>net<span style="color:#999999">.</span>Socket<span style="color:#999999">;</span>

<span style="color:#cc99cc">public</span> <span style="color:#cc99cc">class</span> BIOFileClientDemo <span style="color:#999999">{</span>
    <span style="color:#cc99cc">public</span> <span style="color:#cc99cc">static</span> <span style="color:#cc99cc">void</span> <span style="color:#6699cc">main</span><span style="color:#999999">(</span>String<span style="color:#999999">[</span><span style="color:#999999">]</span> args<span style="color:#999999">)</span> <span style="color:#999999">{</span>
        <span style="color:#cc99cc">try</span> <span style="color:#999999">{</span>
            <span style="color:#999999">// 1. 客户端连接服务器</span>
            Socket socket <span style="color:#99cc99">=</span> <span style="color:#cc99cc">new</span> Socket<span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
            InetSocketAddress inetSocketAddress <span style="color:#99cc99">=</span>
                    <span style="color:#cc99cc">new</span> InetSocketAddress<span style="color:#999999">(</span>Inet4Address<span style="color:#999999">.</span><span style="color:#6699cc">getLocalHost</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">,</span> <span style="color:#f99157">8888</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
            socket<span style="color:#999999">.</span><span style="color:#6699cc">connect</span><span style="color:#999999">(</span>inetSocketAddress<span style="color:#999999">)</span><span style="color:#999999">;</span>
            System<span style="color:#999999">.</span>out<span style="color:#999999">.</span><span style="color:#6699cc">println</span><span style="color:#999999">(</span><span style="color:#99cc99">"客户端连接服务器成功, 开始传输文件 ..."</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
            <span style="color:#cc99cc">long</span> startTime <span style="color:#99cc99">=</span> System<span style="color:#999999">.</span><span style="color:#6699cc">currentTimeMillis</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>

            <span style="color:#999999">// 2. 从文件中读取数据数据并传给服务器</span>
            FileInputStream fileInputStream <span style="color:#99cc99">=</span> <span style="color:#cc99cc">new</span> FileInputStream<span style="color:#999999">(</span><span style="color:#99cc99">"book.pdf"</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
            <span style="color:#cc99cc">byte</span><span style="color:#999999">[</span><span style="color:#999999">]</span> buffer <span style="color:#99cc99">=</span> <span style="color:#cc99cc">new</span> byte<span style="color:#999999">[</span><span style="color:#f99157">4096</span><span style="color:#999999">]</span><span style="color:#999999">;</span>

            <span style="color:#cc99cc">int</span> readLen<span style="color:#999999">;</span>
            <span style="color:#999999">// 读取的字节个数大于等于 0 才写出数据</span>
            <span style="color:#cc99cc">while</span> <span style="color:#999999">(</span> <span style="color:#999999">(</span> readLen <span style="color:#99cc99">=</span> fileInputStream<span style="color:#999999">.</span><span style="color:#6699cc">read</span><span style="color:#999999">(</span>buffer<span style="color:#999999">)</span> <span style="color:#999999">)</span> <span style="color:#99cc99">>=</span> <span style="color:#f99157">0</span> <span style="color:#999999">)</span> <span style="color:#999999">{</span>
                <span style="color:#999999">// 写出数据到服务器</span>
                socket<span style="color:#999999">.</span><span style="color:#6699cc">getOutputStream</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#6699cc">write</span><span style="color:#999999">(</span>buffer<span style="color:#999999">,</span> <span style="color:#f99157">0</span><span style="color:#999999">,</span> readLen<span style="color:#999999">)</span><span style="color:#999999">;</span>
            <span style="color:#999999">}</span>
            System<span style="color:#999999">.</span>out<span style="color:#999999">.</span><span style="color:#6699cc">println</span><span style="color:#999999">(</span><span style="color:#99cc99">"文件传输完毕, 用时 : "</span> <span style="color:#99cc99">+</span> <span style="color:#999999">(</span>System<span style="color:#999999">.</span><span style="color:#6699cc">currentTimeMillis</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#99cc99">-</span> startTime<span style="color:#999999">)</span> <span style="color:#99cc99">+</span> <span style="color:#99cc99">" ms"</span><span style="color:#999999">)</span><span style="color:#999999">;</span>

            <span style="color:#999999">//3. 关闭连接</span>
            socket<span style="color:#999999">.</span><span style="color:#6699cc">close</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
            fileInputStream<span style="color:#999999">.</span><span style="color:#6699cc">close</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
        <span style="color:#999999">}</span> <span style="color:#cc99cc">catch</span> <span style="color:#999999">(</span>IOException e<span style="color:#999999">)</span> <span style="color:#999999">{</span>
            e<span style="color:#999999">.</span><span style="color:#6699cc">printStackTrace</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
        <span style="color:#999999">}</span>
    <span style="color:#999999">}</span>
<span style="color:#999999">}</span>
</code></span></span>
  • 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

3 . 代码运行 :

① 开启服务器 : 服务器开启后阻塞监听 ;

在这里插入图片描述

② 开启客户端 : 客户端开启 , 连接服务器 , 连接成功后 , 将 20M 的文件传输给服务器 ; 客户端用时 229 ms 将数据传输给服务器 , 服务器用时 229 ms 接收并存储数据 , 二者时间基本差不多 ;

在这里插入图片描述
在这里插入图片描述




 

三、 零拷贝案例 服务器端


1 . 阻塞模式 与 非阻塞模式 :

① 非阻塞模式 : 如果调用 服务器套接字通道 ( ServerSocketChannel ) 的 configureBlocking(false) 方法设置非阻塞模式 , 就需要使用 Selector 注册通道 , 并监听事件 ;

② 阻塞模式 : 如果不经过上述设置 , 只需要使用如下方式 , 调用 accept() 方法阻塞等待客户端连接 , 如下用法 ; 这是 服务器套接字通道 ( ServerSocketChannel ) 的阻塞模式的使用 , 这里只是为了演示零拷贝机制 , 代码从简 ;

2 . 零拷贝操作 : 将 Socket 缓冲区中的数据直接拷贝到 内核缓冲区中 , 然后写出到文件 ;

使用零拷贝机制 , 一行代码完成 20M 的文件从 Socket 接收到硬盘文件写出操作 ;

<span style="color:#000000"><span style="background-color:#2d2d2d"><code class="language-java">fileChannel<span style="color:#999999">.</span><span style="color:#6699cc">transferFrom</span><span style="color:#999999">(</span>socketChannel<span style="color:#999999">,</span> <span style="color:#f99157">0</span><span style="color:#999999">,</span> <span style="color:#f99157">1024</span> <span style="color:#99cc99">*</span> <span style="color:#f99157">1024</span> <span style="color:#99cc99">*</span> <span style="color:#f99157">32</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
</code></span></span>
  • 1

3 . 代码示例 :

<span style="color:#000000"><span style="background-color:#2d2d2d"><code class="language-java"><span style="color:#cc99cc">package</span> kim<span style="color:#999999">.</span>hsl<span style="color:#999999">.</span>nio<span style="color:#999999">.</span>zerocopy<span style="color:#999999">;</span>

<span style="color:#cc99cc">import</span> java<span style="color:#999999">.</span>io<span style="color:#999999">.</span>FileOutputStream<span style="color:#999999">;</span>
<span style="color:#cc99cc">import</span> java<span style="color:#999999">.</span>io<span style="color:#999999">.</span>IOException<span style="color:#999999">;</span>
<span style="color:#cc99cc">import</span> java<span style="color:#999999">.</span>net<span style="color:#999999">.</span>InetSocketAddress<span style="color:#999999">;</span>
<span style="color:#cc99cc">import</span> java<span style="color:#999999">.</span>nio<span style="color:#999999">.</span>channels<span style="color:#999999">.</span>FileChannel<span style="color:#999999">;</span>
<span style="color:#cc99cc">import</span> java<span style="color:#999999">.</span>nio<span style="color:#999999">.</span>channels<span style="color:#999999">.</span>ServerSocketChannel<span style="color:#999999">;</span>
<span style="color:#cc99cc">import</span> java<span style="color:#999999">.</span>nio<span style="color:#999999">.</span>channels<span style="color:#999999">.</span>SocketChannel<span style="color:#999999">;</span>

<span style="color:#cc99cc">public</span> <span style="color:#cc99cc">class</span> NIOFileServerDemo <span style="color:#999999">{</span>
    <span style="color:#cc99cc">public</span> <span style="color:#cc99cc">static</span> <span style="color:#cc99cc">void</span> <span style="color:#6699cc">main</span><span style="color:#999999">(</span>String<span style="color:#999999">[</span><span style="color:#999999">]</span> args<span style="color:#999999">)</span> <span style="color:#999999">{</span>
        <span style="color:#cc99cc">try</span> <span style="color:#999999">{</span>
            <span style="color:#999999">// 1. 创建并配置 服务器套接字通道 ServerSocketChannel</span>
            ServerSocketChannel serverSocketChannel <span style="color:#99cc99">=</span> ServerSocketChannel<span style="color:#999999">.</span><span style="color:#6699cc">open</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
            serverSocketChannel<span style="color:#999999">.</span><span style="color:#6699cc">socket</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#6699cc">bind</span><span style="color:#999999">(</span><span style="color:#cc99cc">new</span> InetSocketAddress<span style="color:#999999">(</span><span style="color:#f99157">8888</span><span style="color:#999999">)</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
            <span style="color:#999999">// 注意这里使用阻塞模式, 不调用该代码</span>
            <span style="color:#999999">//serverSocketChannel.configureBlocking(false);</span>
            <span style="color:#999999">// 2. 获取文件通道</span>
            FileChannel fileChannel <span style="color:#99cc99">=</span> <span style="color:#cc99cc">new</span> FileOutputStream<span style="color:#999999">(</span><span style="color:#99cc99">"book2.pdf"</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#6699cc">getChannel</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>

            <span style="color:#999999">// 3. 阻塞等待</span>
            SocketChannel socketChannel <span style="color:#99cc99">=</span> serverSocketChannel<span style="color:#999999">.</span><span style="color:#6699cc">accept</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>

            <span style="color:#999999">// 4. 零拷贝核心操作</span>
            fileChannel<span style="color:#999999">.</span><span style="color:#6699cc">transferFrom</span><span style="color:#999999">(</span>socketChannel<span style="color:#999999">,</span> <span style="color:#f99157">0</span><span style="color:#999999">,</span> <span style="color:#f99157">1024</span> <span style="color:#99cc99">*</span> <span style="color:#f99157">1024</span> <span style="color:#99cc99">*</span> <span style="color:#f99157">32</span><span style="color:#999999">)</span><span style="color:#999999">;</span>

            <span style="color:#999999">// 5. 释放资源</span>
            <span style="color:#999999">//socketChannel.close();</span>
            <span style="color:#999999">//fileChannel.close();</span>
        <span style="color:#999999">}</span> <span style="color:#cc99cc">catch</span> <span style="color:#999999">(</span>IOException e<span style="color:#999999">)</span> <span style="color:#999999">{</span>
            e<span style="color:#999999">.</span><span style="color:#6699cc">printStackTrace</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
        <span style="color:#999999">}</span>
    <span style="color:#999999">}</span>
<span style="color:#999999">}</span>
</code></span></span>
  • 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




 

四、 零拷贝案例 客户端


1 . 零拷贝操作 : 调用 transferTo 方法 , 可以直接将硬盘中的文件传输到服务器端 ;

该方法传输速度快的原理就是使用了零拷贝的机制 , 从文件系统直接拷贝到目标通道 ;

<span style="color:#000000"><span style="background-color:#2d2d2d"><code class="language-java">fileChannel<span style="color:#999999">.</span><span style="color:#6699cc">transferTo</span><span style="color:#999999">(</span>totalCount<span style="color:#999999">,</span> <span style="color:#f99157">1024</span> <span style="color:#99cc99">*</span> <span style="color:#f99157">1024</span> <span style="color:#99cc99">*</span> <span style="color:#f99157">32</span><span style="color:#999999">,</span> socketChannel<span style="color:#999999">)</span>
</code></span></span>
  • 1

2 . 代码示例 :

<span style="color:#000000"><span style="background-color:#2d2d2d"><code class="language-java"><span style="color:#cc99cc">package</span> kim<span style="color:#999999">.</span>hsl<span style="color:#999999">.</span>nio<span style="color:#999999">.</span>zerocopy<span style="color:#999999">;</span>

<span style="color:#cc99cc">import</span> java<span style="color:#999999">.</span>io<span style="color:#999999">.</span>FileInputStream<span style="color:#999999">;</span>
<span style="color:#cc99cc">import</span> java<span style="color:#999999">.</span>io<span style="color:#999999">.</span>IOException<span style="color:#999999">;</span>
<span style="color:#cc99cc">import</span> java<span style="color:#999999">.</span>net<span style="color:#999999">.</span>InetSocketAddress<span style="color:#999999">;</span>
<span style="color:#cc99cc">import</span> java<span style="color:#999999">.</span>nio<span style="color:#999999">.</span>channels<span style="color:#999999">.</span>FileChannel<span style="color:#999999">;</span>
<span style="color:#cc99cc">import</span> java<span style="color:#999999">.</span>nio<span style="color:#999999">.</span>channels<span style="color:#999999">.</span>SocketChannel<span style="color:#999999">;</span>

<span style="color:#cc99cc">public</span> <span style="color:#cc99cc">class</span> NIOFileClientDemo <span style="color:#999999">{</span>
    <span style="color:#cc99cc">public</span> <span style="color:#cc99cc">static</span> <span style="color:#cc99cc">void</span> <span style="color:#6699cc">main</span><span style="color:#999999">(</span>String<span style="color:#999999">[</span><span style="color:#999999">]</span> args<span style="color:#999999">)</span> <span style="color:#999999">{</span>
        <span style="color:#cc99cc">try</span> <span style="color:#999999">{</span>
            <span style="color:#999999">// 1. 创建并配置 服务器套接字通道 ServerSocketChannel</span>
            SocketChannel socketChannel <span style="color:#99cc99">=</span> SocketChannel<span style="color:#999999">.</span><span style="color:#6699cc">open</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
            socketChannel<span style="color:#999999">.</span><span style="color:#6699cc">connect</span><span style="color:#999999">(</span><span style="color:#cc99cc">new</span> InetSocketAddress<span style="color:#999999">(</span><span style="color:#99cc99">"127.0.0.1"</span><span style="color:#999999">,</span> <span style="color:#f99157">8888</span><span style="color:#999999">)</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
            <span style="color:#999999">//socketChannel.configureBlocking(false);</span>

            <span style="color:#999999">// 2. 从文件输入流中获取文件通道 ( FileChannel )</span>
            FileChannel fileChannel <span style="color:#99cc99">=</span> <span style="color:#cc99cc">new</span> FileInputStream<span style="color:#999999">(</span><span style="color:#99cc99">"book.pdf"</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#6699cc">getChannel</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
            <span style="color:#cc99cc">long</span> startTime <span style="color:#99cc99">=</span> System<span style="color:#999999">.</span><span style="color:#6699cc">currentTimeMillis</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>

            <span style="color:#999999">// 3. 零拷贝传输数据, 注意记录每次拷贝的起始位置</span>
            <span style="color:#cc99cc">long</span> transferLen<span style="color:#999999">;</span>
            <span style="color:#cc99cc">long</span> totalCount <span style="color:#99cc99">=</span> <span style="color:#f99157">0</span><span style="color:#999999">;</span>
            <span style="color:#999999">// 使用零拷贝将文件数据传到服务器, 循环终止条件是传输结果小于等于 0</span>
            <span style="color:#cc99cc">while</span> <span style="color:#999999">(</span> <span style="color:#999999">(</span> transferLen <span style="color:#99cc99">=</span> fileChannel<span style="color:#999999">.</span><span style="color:#6699cc">transferTo</span><span style="color:#999999">(</span>totalCount<span style="color:#999999">,</span> <span style="color:#f99157">1024</span> <span style="color:#99cc99">*</span> <span style="color:#f99157">1024</span> <span style="color:#99cc99">*</span> <span style="color:#f99157">32</span><span style="color:#999999">,</span> socketChannel<span style="color:#999999">)</span> <span style="color:#999999">)</span> <span style="color:#99cc99">></span> <span style="color:#f99157">0</span> <span style="color:#999999">)</span> <span style="color:#999999">{</span>
                totalCount <span style="color:#99cc99">+=</span> transferLen<span style="color:#999999">;</span>
            <span style="color:#999999">}</span>

            System<span style="color:#999999">.</span>out<span style="color:#999999">.</span><span style="color:#6699cc">println</span><span style="color:#999999">(</span><span style="color:#99cc99">"文件传输完毕, 用时 : "</span> <span style="color:#99cc99">+</span> <span style="color:#999999">(</span>System<span style="color:#999999">.</span><span style="color:#6699cc">currentTimeMillis</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#99cc99">-</span> startTime<span style="color:#999999">)</span> <span style="color:#99cc99">+</span> <span style="color:#99cc99">" ms"</span><span style="color:#999999">)</span><span style="color:#999999">;</span>

            <span style="color:#999999">// 4. 关闭连接</span>
            socketChannel<span style="color:#999999">.</span><span style="color:#6699cc">close</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
            fileChannel<span style="color:#999999">.</span><span style="color:#6699cc">close</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
        <span style="color:#999999">}</span> <span style="color:#cc99cc">catch</span> <span style="color:#999999">(</span>IOException e<span style="color:#999999">)</span> <span style="color:#999999">{</span>
            e<span style="color:#999999">.</span><span style="color:#6699cc">printStackTrace</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
        <span style="color:#999999">}</span>
    <span style="color:#999999">}</span>
<span style="color:#999999">}</span>
</code></span></span>
  • 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




 

五、 零拷贝案例 运行与分析


1 . 运行代码 :

① 首先运行服务器程序 : 启动即可 ;

② 再运行客户端程序 : 此时会记录整体的运行事件 , 此时从客户端向服务器端传输 20M 文件用时 68ms ;

在这里插入图片描述

2 . NIO 零拷贝 与 BIO 传统拷贝对比 :

BIO 传统拷贝 从客户端向服务器端传输 20MB 文件需要 229 ms ;

NIO 的零拷贝 从客户端向服务器端传输 20MB 文件需要 68ms ;

显然 NIO 零拷贝 传输效率有极大的提升 ;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值