一、问题引出
传统文件拷贝过程

它将硬盘上的文件读取到内核空间的缓冲区中,然后再将缓冲区内容拷贝到Socket缓冲区再拷贝到用户缓冲区中,调用写的方法时会从用户缓冲区到内核传冲区到Socket缓冲区再到服务器端的拷贝,总而言之会进行多次拷贝。
显然,会拥有极大的性能损耗。
简化拷贝过程

通过sendfile将用户空间切换到内核空间,从而节省了用户缓冲区的操作,直接由内核和硬件的对话,但是仍然会有拷贝的过程,这时候就要依赖操作系统的操作了。
零拷贝过程

经过操作系统的配合实现了零拷贝的过程,即通过scatter/gather的方法。
二、代码实验
老式拷贝文件
OldeIOServer
package com.demo.zerocopy;import java.io.DataInputStream;import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;/*** @Description:* @author: HandSomeMaker* @date: 2019/12/30 15:04*/public class OldIoServer {public static void main(String[] args) {try {ServerSocket serverSocket = new ServerSocket(9999);System.out.println("服务器已启动");while (true) {Socket socket = serverSocket.accept();System.out.println("客户端【"+socket.getInetAddress()+"】已连接到服务器");DataInputStream inputStream = new DataInputStream(socket.getInputStream());byte[] bytes = new byte[1024];while (inputStream.read(bytes) > 0) {}}} catch (IOException e) {e.printStackTrace();}}}
OldIOClient
package com.demo.zerocopy;import java.io.*;import java.net.Socket;/*** @Description:* @author: HandSomeMaker* @date: 2019/12/30 15:05*/public class OldIoClient {public static void main(String[] args) {try {Socket socket = new Socket("localhost", 9999);File file = new File("C:" + File.separator + "Program Files" + File.separator + "CCleaner" + File.separator + "CCleaner64.exe");InputStream inputStream = new FileInputStream(file);DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream());byte[] bytes = new byte[1024];long start = System.currentTimeMillis();while (inputStream.read(bytes, 0, bytes.length) > 0) {outputStream.write(bytes);}System.out.println("传送完成,耗时:" + (System.currentTimeMillis() - start));outputStream.close();inputStream.close();socket.close();} catch (IOException e) {e.printStackTrace();}}}
新式拷贝文件
NewIOServer
package com.demo.zerocopy;import java.io.IOException;import java.net.InetSocketAddress;import java.net.ServerSocket;import java.nio.ByteBuffer;import java.nio.channels.ServerSocketChannel;import java.nio.channels.SocketChannel;/*** @Description:* @author: HandSomeMaker* @date: 2019/12/30 15:05*/public class NewIoServer {public static void main(String[] args) {try {ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();ServerSocket serverSocket=serverSocketChannel.socket();//开启端口复用serverSocket.setReuseAddress(true);serverSocket.bind(new InetSocketAddress(9999));System.out.println("服务器已启动");ByteBuffer byteBuffer = ByteBuffer.allocate(1024);while (true) {SocketChannel socketChannel = serverSocketChannel.accept();System.out.println("客户端已连接到服务器");//开启阻塞socketChannel.configureBlocking(true);while (socketChannel.read(byteBuffer) > 0) {byteBuffer.clear();}}} catch (IOException e) {e.printStackTrace();}}}
NewIOClient
package com.demo.zerocopy;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.channels.FileChannel;import java.nio.channels.SocketChannel;/*** @Description:* @author: HandSomeMaker* @date: 2019/12/30 15:05*/public class NewIoClient {public static void main(String[] args) {try {SocketChannel socketChannel = SocketChannel.open();socketChannel.connect(new InetSocketAddress("localhost", 9999));socketChannel.configureBlocking(true);File file = new File("C:" + File.separator + "Program Files" + File.separator + "CCleaner" + File.separator + "CCleaner64.exe");FileChannel fileChannel = new FileInputStream(file).getChannel();long start = System.currentTimeMillis();//将一个通道和另一个通道直接相连接fileChannel.transferTo(0, fileChannel.size(), socketChannel);System.out.println("传送完成,耗时:" + (System.currentTimeMillis() - start));socketChannel.close();fileChannel.close();} catch (IOException e) {e.printStackTrace();}}}
效果演示
老式拷贝文件
新式拷贝文件
总结
显然,新式拷贝文件要比老式拷贝快上成倍的速度。
但是也不绝对,详情请查看https://www.cnblogs.com/interdrp/p/3785164.html
