【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(); } } }
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 文件 ,
將其傳輸到服務器中 ;
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(); } } }
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 接收到硬盤文件寫出操作 ;
fileChannel.transferFrom(socketChannel, 0, 1024 * 1024 * 32);
1
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
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 方法 , 可以直接將硬盤中的文件傳輸到服務器端 ;
該方法傳輸速度快的原理就是使用了零拷貝的機制 , 從文件系統直接拷貝到目標通道 ;
fileChannel.transferTo(totalCount, 1024 * 1024 * 32, socketChannel)
1
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
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 零拷貝 傳輸效率有極大的提升 ;
Socket編程
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。