共计 3171 个字符,预计需要花费 8 分钟才能阅读完成。
什么是零拷贝?我们首先来认识一下传统的 I / O 操作。假如说用户进程现在要把一个文件复制到另一个地方。那么用户程序必须先把这个文件读入内存,然后再把内存里的数据写入另一个文件。不过文件读入内存也不是直接读入用户进程的内存,而是先读入操作系统内核的内存,然后再从操作系统内核的内存区读到用户进程的内存。与之对应的是,写文件也不是直接写到磁盘上的文件,而是用户进程先把自己内存的数据传到操作系统内核的内存,然后再从操作系统内核的内存区写到磁盘。而这其中涉及到诸多的系统调用。因此看上去简单的操作至少要分为四部 1 磁盘文件读入操作系统 2 操作系统读到用户进程 3 用户进程写到操作系统 4 操作系统写入磁盘文件
零拷贝和传统 I / O 有和不同?零拷贝就是指,传输一个文件的时候,不需要把文件读到用户进程再处理,而是直接把文件读到操作系统一个内存区,然后再移动到操作系统的另一个内存区,最后写入文件。这样一来,步骤变成这样:1 磁盘文件读入操作系统 2 操作系统把数据写入操作系统另一个区域 3 操作系统写入磁盘文件虽然只少了一步,但是这里不仅减少了数据移动的时间损耗,而且减少了系统调用的次数,因此大大缩短了时间。更加详细的解释请看 https://blog.csdn.net/u010530…
java 里如何实现零拷贝呢?这就要说起 java nio 中的 FileChannel.transferTo()方法了,该方法是把 FileChannel 中的数据利用零靠的技术转移到另一个 channel。这另一个 channel 往往是 FileChannel,不过 SocketChannel 也是可以的:)。简单实现 (静态下载文件,不能根据用户指令来更改下载的文件。) 代码如下:单线程版本:
package qiuqi.filedownloadtest;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.*;
import java.util.Iterator;
public class FileServer {
public static void main(String[] args) throws IOException {
startServer();
}
public static void startServer() throws IOException {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(9999));
serverSocketChannel.configureBlocking(false);
Selector selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (selector.select() > 0)
{
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext())
{
SelectionKey key = iterator.next();
iterator.remove();
if(key.isAcceptable())
{
SocketChannel socketChannel = serverSocketChannel.accept();
try (FileInputStream in = new FileInputStream(“C:\\Users\\dell\\Desktop\\ZOL 手机数据(1).rar”)){
in.getChannel().transferTo(0,in.getChannel().size(),socketChannel);
socketChannel.close();
}
catch (IOException e){e.printStackTrace();}
}
}
}
}
}
多线程版本:
package qiuqi.filedownloadtest;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class FileServer {
static ExecutorService threadpool = Executors.newCachedThreadPool();
public static void main(String[] args) throws IOException {
startServer();
}
public static void startServer() throws IOException {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(9999));
serverSocketChannel.configureBlocking(false);
Selector selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (selector.select() > 0)
{
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext())
{
SelectionKey key = iterator.next();
iterator.remove();
if(key.isAcceptable())
{
SocketChannel socketChannel = serverSocketChannel.accept();
threadpool.execute(new Runnable() {
@Override
public void run() {
try (FileInputStream in = new FileInputStream(“C:\\Users\\dell\\Desktop\\ZOL 手机数据(1).rar”)){
in.getChannel().transferTo(0,in.getChannel().size(),socketChannel);
socketChannel.close();
}
catch (IOException e){e.printStackTrace();}
}
});
}
}
}
}
}
代码就不讲解了。如果学过 java nio,那么理解上面的程序轻而易举。如果不熟悉 java nio 的服务器编程那么请先学习再来观看。
最后我想说,java NIO 真的是 NEW IO 即新的 IO,而不是 NonBlocking IO 即非阻塞 IO。因为在这套体系里,不仅仅提供了非阻塞的编程模型,而且提供了类似零拷贝,内存映射这样的新技术(对于操作系统来说早就有了)。