基於零拷貝技術的的java NIO文件下載服務器

什麼是零拷貝?
咱們首先來認識一下傳統的I/O操做
假如說用戶進程如今要把一個文件複製到另外一個地方。
那麼用戶程序必須先把這個文件讀入內存,而後再把內存裏的數據寫入另外一個文件。
不過文件讀入內存也不是直接讀入用戶進程的內存,而是先讀入操做系統內核的內存,而後再從操做系統內核的內存區讀到用戶進程的內存。
與之對應的是,寫文件也不是直接寫到磁盤上的文件,而是用戶進程先把本身內存的數據傳到操做系統內核的內存,而後再從操做系統內核的內存區寫到磁盤。而這其中涉及到諸多的系統調用
所以看上去簡單的操做至少要分爲四部
1磁盤文件讀入操做系統
2操做系統讀到用戶進程
3用戶進程寫到操做系統
4操做系統寫入磁盤文件java

零拷貝和傳統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")){

                         long size = in.available();
                         long num = 0;
                         long begin = 0;
                         while ( (num = in.getChannel().transferTo(begin,size,socketChannel))!=0)
                         {
                             size-=num;
                             begin += num;
                         }
                     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")){

                         long size = in.available();
                         long num = 0;
                         long begin = 0;
                         while ( (num = in.getChannel().transferTo(begin,size,socketChannel))!=0)
                         {
                             size-=num;
                             begin += num;
                         }
                                socketChannel.close();
                            }
                            catch (IOException e){e.printStackTrace();}
                        }
                    });
                 

                }
            }
        }


    }
}

代碼就不講解了。若是學過java nio,那麼理解上面的程序垂手可得。
若是不熟悉java nio的服務器編程那麼請先學習再來觀看。socket

最後我想說,java NIO真的是NEW IO即新的IO,而不是NonBlocking IO即非阻塞IO。由於在這套體系裏,不單單提供了非阻塞的編程模型,並且提供了相似零拷貝,內存映射這樣的新技術(對於操做系統來講早就有了)。ide

相關文章
相關標籤/搜索