哦哇資訊網

Java教程:NIO的基本用法

由 黑馬程式設計師 發表于 美食2023-01-08

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

Java教程:NIO的基本用法

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

Java教程:NIO的基本用法

標記和恢復

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();

Java教程:NIO的基本用法

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,也就是可以管理多個網路連結。

Java教程:NIO的基本用法

使用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 selectedKeys = selector。selectedKeys(); Iterator it = selectedKeys。iterator(); while(it。hasNext()) { SelectionKey key = it。next(); if(key。isAcceptable()) { // 接受連線 } else if (key。isReadable()) { // 通道可讀 } else if (key。isWritable()) { // 通道可寫 } it。remove(); }}

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 selectedKeys = selector。selectedKeys(); Iterator it = selectedKeys。iterator(); while (it。hasNext()) { SelectionKey key = it。next(); SocketChannel socketChannel1=null; SocketChannel socketChannel2=null; if (key。isAcceptable()) { System。out。println(“isAcceptable”); // 建立新的連線,並且把連線註冊到selector上,而且, // 宣告這個channel只對讀操作感興趣。 socketChannel1 = ssc。accept(); socketChannel1。configureBlocking(false); socketChannel1。register(selector, SelectionKey。OP_READ); UUID uuid = UUID。randomUUID(); fosChannel=new FileOutputStream(“day05/src/”+uuid+“。txt”)。getChannel(); } else if (key。isReadable()) { System。out。println(“isReadable”); // 通道可讀 socketChannel2 = (SocketChannel) key。channel(); //建立本地通道,用於往檔案中寫資料 ByteBuffer readBuff=ByteBuffer。allocate(1024); while (socketChannel2。read(readBuff)>0){ readBuff。flip(); //準備把緩衝區資料輸出 fosChannel。write(readBuff); readBuff。clear();//準備讀取資料到緩衝區 } fosChannel。close(); key。interestOps(SelectionKey。OP_WRITE); } else if (key。isWritable()) { System。out。println(“isWritable”); // 通道可寫 ByteBuffer writeBuff=ByteBuffer。allocate(1024); writeBuff。put(“上傳成功”。getBytes()); writeBuff。flip(); SocketChannel socketChannel = (SocketChannel) key。channel(); socketChannel。write(writeBuff); key。interestOps(SelectionKey。OP_READ); } it。remove(); } } } catch (Exception e) { //e。printStackTrace(); } finally { } }}

TAG: 緩衝區positionoutprintlnbyteBuffer