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 零拷贝 传输效率有极大的提升 ;
本文通过对比NIO的零拷贝与BIO传统拷贝在传输20MB文件时的时间消耗,展示了零拷贝技术显著提高的传输效率,服务器端和客户端实现代码详析。
586

被折叠的 条评论
为什么被折叠?



