【Netty】零拷贝案例 ( transferTo | transferFrom )
文章目錄
- 一、 案例需求
- 二、 傳統 BIO 拷貝案例
- 三、 零拷貝案例 服務器端
- 四、 零拷貝案例 客戶端
- 五、 零拷貝案例 運行與分析
一、 案例需求
給出兩個案例 , 一個是 使用普通的 BIO 模型 傳輸文件的案例 , 一個是 NIO + 零拷貝 傳輸文件案例 ;
傳輸 20M 的文件 , 對比二者的傳輸效率 ;
二、 傳統 BIO 拷貝案例
服務器端使用 ServerSocket , 客戶端使用 Socket , 在客戶端將文件傳輸給服務器端 , 并統計整體的時間消耗 ;
1 . 服務器端代碼 : 服務器端程序啟動后 , 監聽 8888 端口 , 等待客戶端連接 , 客戶端連接成功后 , 讀取客戶端上傳的數據 , 服務器端將接收到的數據存儲在 book2.pdf 文件中 ;
package kim.hsl.nio.zerocopy;import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket;public class BIOFileServerDemo {public static void main(String[] args) {try {// 1. 創建服務器套接字, 并等待客戶端連接ServerSocket serverSocket = new ServerSocket(8888);System.out.println("服務器啟動,監聽 8888 端口");//阻塞, 等待客戶端連接請求 ( 此處是第一個阻塞點 )Socket socket = serverSocket.accept();long startTime = System.currentTimeMillis();System.out.println("客戶端連接成功");// 2. 接收客戶端傳輸的數據, 并寫出到文件中InputStream inputStream = socket.getInputStream();FileOutputStream fileOutputStream = new FileOutputStream("book2.pdf");byte[] buffer = new byte[4096];int readLen;// 讀取的字節個數大于等于 0 才寫出數據while ( ( readLen = inputStream.read(buffer) ) >= 0 ) {// 寫出數據到服務器fileOutputStream.write(buffer, 0, readLen);}System.out.println("文件傳輸完畢, 用時 : " + (System.currentTimeMillis() - startTime) + " ms");// 3. 關閉流socket.close();inputStream.close();fileOutputStream.close();} catch (IOException e) {e.printStackTrace();}} }2 . 客戶端代碼 : 客戶端連接本地的 8888 端口服務器 , 讀取本地的 book.pdf 文件 , 將其傳輸到服務器中 ;
package kim.hsl.nio.zerocopy;import java.io.FileInputStream; import java.io.IOException; import java.net.Inet4Address; import java.net.InetSocketAddress; import java.net.Socket;public class BIOFileClientDemo {public static void main(String[] args) {try {// 1. 客戶端連接服務器Socket socket = new Socket();InetSocketAddress inetSocketAddress =new InetSocketAddress(Inet4Address.getLocalHost(), 8888);socket.connect(inetSocketAddress);System.out.println("客戶端連接服務器成功, 開始傳輸文件 ...");long startTime = System.currentTimeMillis();// 2. 從文件中讀取數據數據并傳給服務器FileInputStream fileInputStream = new FileInputStream("book.pdf");byte[] buffer = new byte[4096];int readLen;// 讀取的字節個數大于等于 0 才寫出數據while ( ( readLen = fileInputStream.read(buffer) ) >= 0 ) {// 寫出數據到服務器socket.getOutputStream().write(buffer, 0, readLen);}System.out.println("文件傳輸完畢, 用時 : " + (System.currentTimeMillis() - startTime) + " ms");//3. 關閉連接socket.close();fileInputStream.close();} catch (IOException e) {e.printStackTrace();}} }3 . 代碼運行 :
① 開啟服務器 : 服務器開啟后阻塞監聽 ;
② 開啟客戶端 : 客戶端開啟 , 連接服務器 , 連接成功后 , 將 20M 的文件傳輸給服務器 ; 客戶端用時 229 ms 將數據傳輸給服務器 , 服務器用時 229 ms 接收并存儲數據 , 二者時間基本差不多 ;
三、 零拷貝案例 服務器端
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. 創建并配置 服務器套接字通道 ServerSocketChannelServerSocketChannel 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. 創建并配置 服務器套接字通道 ServerSocketChannelSocketChannel 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;// 使用零拷貝將文件數據傳到服務器, 循環終止條件是傳輸結果小于等于 0while ( ( 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 零拷貝 傳輸效率有極大的提升 ;
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的【Netty】零拷贝案例 ( transferTo | transferFrom )的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Netty】mmap 和 sendFi
- 下一篇: 【Netty】Netty 简介 ( 原生