NIO是New I/O的簡稱,與舊式基於流的I/O相對,從名字上來看,它表示新的一套I/O標準。它是從JDK1。4中被納入到JDK中的。
與舊式的IO流相比,NIO是基於Block的,它以塊為單位來處理資料,最為重要的兩個元件是緩衝區Buffer和通道Channel。緩衝區是一塊連續的記憶體塊,是NIO讀寫資料的載體;通道表示緩衝資料的源頭和目的地,它用於向緩衝區讀取或者寫入資料,是訪問緩衝區的介面。
Buffer的基本原理
Buffer中最重要的3個引數:位置(position)、容量(capacity)、上限(limit)。他們3者的含義如下
位置(position): 表示當前緩衝區的位置,從position位置之後開始讀寫資料。容量(capacity): 表示緩衝區的最大容量上限(limit): 表示緩衝區的實際上限,它總是小於或等於容量
緩衝區的容量(capacity)是不變的,而位置(position)和上限(limit)和以根據實際需要而變化。也就是說,可以透過改變當前位置和上限來操作緩衝區內任意位置的資料。
Buffer的常用方法
NIO提供一系列方法來操作Buffer的位置(position)和上限(limit),以及向緩衝區讀寫資料。
put() //向緩衝區position位置新增資料。並且position往後移動,不能超過limit上限。get() //讀取當前position位置的資料。並且position往後移動,不能超過limit上限。flip() //將limit置位為當前position位置,再講position設定為0rewind() //僅將當前position位置設定為0remaining //獲取緩衝區中當前position位置和limit上限之間的元素數(有效的元素數)hasRemaining() //判斷當前緩衝區是否存在有效的元素數mark() //在當前position位置打一個標記reset() //將當前position位置恢復到mark標記的位置。duplicate() //複製緩衝區
建立緩衝區
//建立一個容量為10的緩衝區ByteBuffer byteBuffer1=ByteBuffer。allocate(10);//建立一個包裹資料的緩衝區ByteBuffer byteBuffer2 = ByteBuffer。wrap(“abcde”。getBytes());
獲取/設定緩衝區引數
//建立一個容量為10的緩衝區ByteBuffer byteBuffer=ByteBuffer。allocate(10);System。out。println(“位置:”+byteBuffer。position()); //0System。out。println(“上限:”+byteBuffer。limit()); //10System。out。println(“容量:”+byteBuffer。capacity());//10
新增資料到緩衝區
//建立一個容量為10的緩衝區ByteBuffer byteBuffer=ByteBuffer。allocate(10);//新增資料到緩衝區byteBuffer。put(“abcde”。getBytes());System。out。println(“position位置:”+byteBuffer。position()); //5System。out。println(“limit上限:”+byteBuffer。limit()); //10System。out。println(“capacity容量:”+byteBuffer。capacity()); //10
rewind重置緩衝區
rewind函式將position置為0位置,並清除標記。
//建立一個容量為10的緩衝區ByteBuffer byteBuffer=ByteBuffer。allocate(10);//新增資料到緩衝區byteBuffer。put(“abcde”。getBytes());System。out。println(“position位置:”+byteBuffer。position()); //5System。out。println(“limit上限:”+byteBuffer。limit()); //10System。out。println(“capacity容量:”+byteBuffer。capacity()); //10System。out。println(“——————”);//重置緩衝區byteBuffer。rewind();System。out。println(“position位置:”+byteBuffer。position()); //0System。out。println(“limit上限:”+byteBuffer。limit()); //10System。out。println(“capacity容量:”+byteBuffer。capacity());//10
flip重置緩衝區
flip函式現將limit設定為position位置,再將position置為0位置,並清除mar標記。
//建立一個容量為10的緩衝區ByteBuffer byteBuffer=ByteBuffer。allocate(10);//新增資料到緩衝區byteBuffer。put(“abcde”。getBytes());System。out。println(“position位置:”+byteBuffer。position()); //5System。out。println(“limit上限:”+byteBuffer。limit()); //10System。out。println(“capacity容量:”+byteBuffer。capacity()); //10System。out。println(“——————”);//重置緩衝區byteBuffer。flip();System。out。println(“position位置:”+byteBuffer。position()); //0System。out。println(“limit上限:”+byteBuffer。limit()); //5System。out。println(“capacity容量:”+byteBuffer。capacity());//10
clear清空緩衝區
clear方法也將position置為0,同時將limit置為capacity的大小,並清除mark標記。
//建立一個容量為10的緩衝區ByteBuffer byteBuffer=ByteBuffer。allocate(10);//設定上限為5byteBuffer。limit(5);//新增資料到緩衝區byteBuffer。put(“abcde”。getBytes());System。out。println(“position位置:”+byteBuffer。position()); //5System。out。println(“limit上限:”+byteBuffer。limit()); //5System。out。println(“capacity容量:”+byteBuffer。capacity()); //10System。out。println(“——————”);//重置緩衝區byteBuffer。clear();System。out。println(“position位置:”+byteBuffer。position()); //0System。out。println(“limit上限:”+byteBuffer。limit()); //10System。out。println(“capacity容量:”+byteBuffer。capacity());//10
標記和恢復
ByteBuffer buffer = ByteBuffer。allocate(10);//新增資料到緩衝區buffer。put(“abcde”。getBytes());//打一個標記buffer。mark();System。out。println(“標記位置:”+buffer。position()); //5//再新增5個位元組資料到緩衝區buffer。put(“fijkl”。getBytes());System。out。println(“當前位置:”+buffer。position()); //10//將position恢復到mark標記位置buffer。reset();System。out。println(“恢復標記位置:”+buffer。position());//5
FileChannel通道
FileChannel是用於操作檔案的通道,可以用於讀取檔案、也可以寫入檔案
//建立讀取檔案通道FileChannel fisChannel = new FileInputStream(“day05/src/a。txt”)。getChannel();//建立寫入檔案的通道FileChannel fosChannel = new FileOutputStream(“day05/src/b。txt”)。getChannel();//建立緩衝區ByteBuffer buffer = ByteBuffer。allocate(2);while (fisChannel。read(buffer)!=-1){ System。out。println(“position:”+buffer。position()); //0 System。out。println(“limit:”+buffer。limit());//2 //重置緩衝區(為輸出buffer資料做準備) buffer。flip(); fosChannel。write(buffer); //重置緩衝區(為輸入buffer資料做準備) buffer。clear();}//關閉通道fisChannel。close();fosChannel。close();
SocketChannel通道
下面程式碼使用SocketChannel通道上傳檔案到伺服器
public class Client { public static void main(String[] args) throws IOException { //建立通道 SocketChannel socketChannel = SocketChannel。open(new InetSocketAddress(“127。0。0。1”, 10000)); //建立緩衝區 ByteBuffer buffer = ByteBuffer。allocate(1024); //讀取本地檔案資料到緩衝區 FileChannel fisChannel = new FileInputStream(“day05/src/a。txt”)。getChannel(); while (fisChannel。read(buffer)!=-1){ buffer。flip(); //為寫入資料做準備 socketChannel。write(buffer); buffer。clear(); //為讀取資料做準備 } //關閉本地通道 fisChannel。close(); //socketChannel。shutdownOutput(); //讀取服務端回寫的資料 buffer。clear(); int len = socketChannel。read(buffer); System。out。println(new String(buffer。array(), 0, len)); //關閉socket通道 socketChannel。close(); }}
ServerSocketChannel通道
下面程式碼使用ServerSocketChannel通道接收檔案並儲存到伺服器
public class Server { public static void main(String[] args) throws IOException { //1。建立ServerSocketChannel通道 ServerSocketChannel serverSocketChannel = ServerSocketChannel。open(); //2。繫結埠號 serverSocketChannel。bind(new InetSocketAddress(10000)); //3。設定非阻塞 serverSocketChannel。configureBlocking(false); System。out。println(“伺服器已開啟”); while (true) { //4。獲取客戶端通道,如果有客戶端連線返回客戶端通道,否則返回null SocketChannel socketChannel = serverSocketChannel。accept(); if(socketChannel!=null){ socketChannel。configureBlocking(false); //建立本地通道,用於往檔案中寫資料 UUID uuid = UUID。randomUUID(); FileChannel fosChannel=new FileOutputStream(“day05/src/”+uuid+“。txt”)。getChannel(); ByteBuffer buffer=ByteBuffer。allocate(1024); while (socketChannel。read(buffer)>0){ buffer。flip(); //準備把緩衝區資料輸出 fosChannel。write(buffer); buffer。clear();//準備讀取資料到緩衝區 } fosChannel。close(); //回寫資料到客戶端 ByteBuffer resultBuffer=ByteBuffer。wrap(“上傳檔案成功”。getBytes()); socketChannel。write(resultBuffer); //關閉客戶端通道 socketChannel。close(); } } }}
NIO Selector選擇器
Selector
一般稱 為
選擇器
,當然你也可以翻譯為
多路複用器
。它是Java NIO核心元件中的一個,用於檢查一個或多個NIO Channel(通道)的狀態是否處於可讀、可寫。如此可以實現單執行緒管理多個channels,也就是可以管理多個網路連結。
使用Selector的伺服器模板程式碼
有了模板程式碼我們在編寫程式時,大多數時間都是在模板程式碼中新增相應的業務程式碼
ServerSocketChannel ssc = ServerSocketChannel。open();ssc。socket()。bind(new InetSocketAddress(“localhost”, 8080));ssc。configureBlocking(false);Selector selector = Selector。open();ssc。register(selector, SelectionKey。OP_ACCEPT);while(true) { int readyNum = selector。select(); if (readyNum == 0) { continue; } Set
NIO Selector服務端
按照上面的模板程式碼,改寫接收檔案的服務端。
public class Server { public static void main(String[] args) { try { ServerSocketChannel ssc = ServerSocketChannel。open(); ssc。socket()。bind(new InetSocketAddress(“localhost”, 10000)); ssc。configureBlocking(false); Selector selector = Selector。open(); ssc。register(selector, SelectionKey。OP_ACCEPT); FileChannel fosChannel=null; while (true) { int readyNum = selector。select(); System。out。println(readyNum); if (readyNum == 0) { continue; } Set
猜你喜歡
- 2021-12-122款全新SUV、1款電動皮卡、1款敞篷跑車 日產4款純電動概念車
- 2021-07-18讓人暖心的溫柔情話,溫柔美好,一見傾心
- 2021-07-01如何用數字表達“我愛你”?521已經out了!現在流行這些
- 2021-06-18Java面試題:O和NIO的區別是什麼?
- 2021-05-30王祖藍被OUT,看到“監獄”裡的東西后大驚,真是羨慕