三、 零拷贝案例 服务器端
1 . 阻塞模式 与 非阻塞模式 :
① 非阻塞模式 : 如果调用 服务器套接字通道 ( ServerSocketChannel ) 的 configureBlocking(false) 方法设置非阻塞模式 , 就需要使用 Selector 注册通道 , 并监听事件 ;
② 阻塞模式 : 如果不经过上述设置 , 只需要使用如下方式 , 调用 accept() 方法阻塞等待客户端连接 , 如下用法 ; 这是 服务器套接字通道 ( ServerSocketChannel ) 的阻塞模式的使用 , 这里只是为了演示零拷贝机制 , 代码从简 ;
2 . 零拷贝操作 : 将 Socket 缓冲区中的数据直接拷贝到 内核缓冲区中 , 然后写出到文件 ;
使用零拷贝机制 , 一行代码完成 20M 的文件从 Socket 接收到硬盘文件写出操作 ;
fileChannel.transferFrom(socketChannel, 0, 1024 * 1024 * 32);
3 . 代码示例 :
package kim.hsl.nio.zerocopy; import java.io.FileOutputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.FileChannel; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; public class NIOFileServerDemo { public static void main(String[] args) { try { // 1. 创建并配置 服务器套接字通道 ServerSocketChannel ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.socket().bind(new InetSocketAddress(8888)); // 注意这里使用阻塞模式, 不调用该代码 //serverSocketChannel.configureBlocking(false); // 2. 获取文件通道 FileChannel fileChannel = new FileOutputStream("book2.pdf").getChannel(); // 3. 阻塞等待 SocketChannel socketChannel = serverSocketChannel.accept(); // 4. 零拷贝核心操作 fileChannel.transferFrom(socketChannel, 0, 1024 * 1024 * 32); // 5. 释放资源 //socketChannel.close(); //fileChannel.close(); } catch (IOException e) { e.printStackTrace(); } } }
四、 零拷贝案例 客户端
1 . 零拷贝操作 : 调用 transferTo 方法 , 可以直接将硬盘中的文件传输到服务器端 ;
该方法传输速度快的原理就是使用了零拷贝的机制 , 从文件系统直接拷贝到目标通道 ;
fileChannel.transferTo(totalCount, 1024 * 1024 * 32, socketChannel)
2 . 代码示例 :
package kim.hsl.nio.zerocopy; import java.io.FileInputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.FileChannel; import java.nio.channels.SocketChannel; public class NIOFileClientDemo { public static void main(String[] args) { try { // 1. 创建并配置 服务器套接字通道 ServerSocketChannel SocketChannel socketChannel = SocketChannel.open(); socketChannel.connect(new InetSocketAddress("127.0.0.1", 8888)); //socketChannel.configureBlocking(false); // 2. 从文件输入流中获取文件通道 ( FileChannel ) FileChannel fileChannel = new FileInputStream("book.pdf").getChannel(); long startTime = System.currentTimeMillis(); // 3. 零拷贝传输数据, 注意记录每次拷贝的起始位置 long transferLen; long totalCount = 0; // 使用零拷贝将文件数据传到服务器, 循环终止条件是传输结果小于等于 0 while ( ( transferLen = fileChannel.transferTo(totalCount, 1024 * 1024 * 32, socketChannel) ) > 0 ) { totalCount += transferLen; } System.out.println("文件传输完毕, 用时 : " + (System.currentTimeMillis() - startTime) + " ms"); // 4. 关闭连接 socketChannel.close(); fileChannel.close(); } catch (IOException e) { e.printStackTrace(); } } }
五、 零拷贝案例 运行与分析
1 . 运行代码 :
① 首先运行服务器程序 : 启动即可 ;
② 再运行客户端程序 : 此时会记录整体的运行事件 , 此时从客户端向服务器端传输 20M 文件用时 68ms ;
2 . NIO 零拷贝 与 BIO 传统拷贝对比 :
BIO 传统拷贝 从客户端向服务器端传输 20MB 文件需要 229 ms ;
NIO 的零拷贝 从客户端向服务器端传输 20MB 文件需要 68ms ;
显然 NIO 零拷贝 传输效率有极大的提升 ;