【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 . 代码运行 :


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

【Netty】零拷贝案例 ( transferTo | transferFrom )_第1张图片


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

【Netty】零拷贝案例 ( transferTo | transferFrom )_第2张图片
【Netty】零拷贝案例 ( transferTo | transferFrom )_第3张图片





三、 零拷贝案例 服务器端



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 ;

【Netty】零拷贝案例 ( transferTo | transferFrom )_第4张图片


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


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

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


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

你可能感兴趣的:(NIO,Netty)