文章目錄
- 1.概述
- 2.緩沖區Buffer定義
- 3.Buffer內部結構
- 4.Buffer常用的操作
- 4.1 allocate
- 4.2 wrap
- 4.3 put
- 4.4 flip
- 4.5 get
- 4.6 mark
- 4.7 reset
- 4.8 clear
- 4.9 rewind
- 4.10 remaining
- 5. chanel
- 5.1 FileChannel 檔案管道
- 5.2 DatagramChannel UDP套接字管道
- 5.3 TCP套接字管道
- 6.后續
1.概述
在BIO API中是通過InputStream 與outPutStream 兩個流進行輸入輸出,而NIO 使用一個雙向通信的管道代替了它倆,管道(Channel)必須依賴緩沖區(Buffer)實作通信,
管道對比流多了一些如:非阻塞、堆外記憶體映射、零拷貝等特性,
2.緩沖區Buffer定義
管道要依賴于緩沖區,所以先來介紹一下緩沖區的概念和使用,
緩沖區內部維護了一個陣列來存盤資料,快取區并不支持存盤的任意的資料型別,只能存盤一些基本資料型別,
具備讀寫、清空的功能,不具備執行緒安全,需要自行的控制執行緒安全問題,
3.Buffer內部結構
拿ByteBuffer舉例,它是Buffer的一個子類,用于存盤Byte型別的資料,其他的緩沖區差不多,只是存盤的資料型別不同,
內部維護了一個byte陣列,用來存盤資料的,
父類Buffer有4個重要的屬性:
-
capacity
表示陣列的容量大小
-
limit
限制Buffer的可讀和可寫的范圍,默認等于capacity
-
position
當前讀寫的位置,默認是0,每讀取一位或寫一位,則+1.
-
mark
做標記,用于reset將position設定到這個位置,默認是-1
4者的位置關系:mark <= position <= limit <= capacity
4.Buffer常用的操作
4.1 allocate
分配緩沖區的存盤空間,初始化所有的屬性,例如:

得到的結果如下:

4.2 wrap
基于陣列包裝一個Buffer,position為0,limit為容量值
測驗代碼如下:

buffer結果如下:

4.3 put
寫入緩沖區,測驗代碼如下:

在put的程序中,會改變position,也就是說每寫入一個數字,就會將position+1.當第7次寫入的時候,position會超過limit的限制,此時會拋出例外BufferOverflowExeception

4.4 flip
為讀取做好準備,在put完之后,執行flip可以重置position,設定成0.
之后的讀取操作就從position的位置開始,
測驗代碼如下:

執行完put 5 之后 buffer的結果如下:

接著執行flip,結果如下:

根據結果就可以看出flip的作用,將position設定成0
并且將limit設定成了原先position的位置,此時的limit就限制了讀的范圍,
4.5 get
讀取緩沖區的內容,每讀取一個,position位置后移動一位
測驗代碼如下:

當讀取到第5個的時候,此時的buffer里面的內容如下:

可以發現此時的position已經到了limit的限制邊界,如果再次讀取就會報錯,讀取越界例外BufferUnderflowException

4.6 mark
設定標記位,記錄下當前的位置
測驗代碼如下:

執行完mark后,將mark屬性設定成position,也就是做了個標記

4.7 reset
reset的作用就是將position的值設定成mark,用于修改緩沖區中的一段資料,或者重復的讀取緩沖區中的一段資料,
測驗代碼如下:

執行完reset之后,buffer結果如下

在記錄mark標記后為,讀取了兩個數字,并將者兩個數字修改了,reset之后,position又回到了mark記錄的位置,此時再進行寫操作,就可以修改緩沖區中的那兩個數字,結果如下

4.8 clear
重置緩沖區的屬性,但是并不會清慷訓沖區里的資料
將position的位置設為0,mark設定為-1.limit設定為capacity
測驗代碼如下:

執行完clear之后,緩沖區的結果如下:

4.9 rewind
為重新讀取做準備,將position設定為0,limit不變,mark設定為-1
測驗代碼如下:

執行完rewind之后,buffer結果如下:

4.10 remaining
回傳還有多少可讀取的范圍
也就是limit-position
測驗代碼如下:

此時limit等于5 讀取了兩次之后,執行remaining方法,回傳的結果是3,表示剩余可讀取的內容
5. chanel
管道用于連接檔案、網路Socket等,它可同時執行讀取和寫入兩個I/O 操作,固稱雙向管道,它有連接和關閉兩個狀態,在創建管道時處于打開狀態,一但關閉 在呼叫I/O操作就會報ClosedChannelException ,通過管道的isOpen 方法可判斷其是否處于打開狀態,
5.1 FileChannel 檔案管道
固名思議它就是用于操作檔案的,除常規操作外它還支持以下特性:
-
支持對檔案的指定區域進行讀寫
-
堆外記憶體映射,進行大檔案讀寫時,可直接映射到JVM宣告記憶體之外,從面提升讀寫效率,
-
零拷貝技術,通過
transferFrom或transferTo直接將資料傳輸到某個通道,極大提高性能, -
鎖定檔案指定區域,以阻止其它程式員進行訪問
打開FileChannel目前只能通過流進行間打開,如inputStream.getChannel() 和outputStream.getChannel() ,通過輸入流打開的管道只能進行取,而outputStream打開的只能寫,否則會分別拋出NonWritableChannelException與NonReadableChannelException例外,
如果想要管道同時支持讀寫,必須用RandomAccessFile 讀寫模式才可以,
下面的測驗代碼是檔案管道的基本使用
//1. 打開檔案管道
FileChannel channel = new RandomAccessFile(file_name,"rw").getChannel();
ByteBuffer buffer=ByteBuffer.allocate(1024); // 宣告1024個空間
// 從檔案中 讀取資料并寫入管道 再寫入緩沖
channel.read(buffer);
buffer.flip();//上面學的,寫完之后,需要將position歸0,為了后面的讀取做準備
byte[] bytes= new byte[buffer.remaining()];
int i =0;
while (buffer.hasRemaining()){
bytes[i++]= buffer.get();
}
System.out.println(new String(bytes));
// 把緩沖區資料寫入到管道
channel.write(ByteBuffer.wrap("森林大帥哥".getBytes()));
channel.close();
5.2 DatagramChannel UDP套接字管道
UDP是無連接的協議,DatagramChannel就是為這個協議提供服務,以接收客戶端發來的訊息,
DatagramChannel的基本用法如下:
public void test1() throws IOException {
DatagramChannel channel=DatagramChannel.open();
// 系結埠
channel.bind(new InetSocketAddress(8080));
ByteBuffer buffer=ByteBuffer.allocate(8192);
while (true){
buffer.clear(); // 清慷訓原
channel.receive(buffer); // 阻塞
buffer.flip();
byte[] bytes=new byte[buffer.remaining()];
buffer.get(bytes);
System.out.println(new String(bytes));
}
}
使用命令nc - uv 127.0.0.1 8080 可以向指定的ip埠號 發送udp

idea控制臺就會輸出如下:

5.3 TCP套接字管道
TCP是一個有連接協議,須建立連接后才能通信,這就需要下面兩個管道:
-
**ServerSocketChannel :**用于與客戶端建立連接
-
**SocketChannel :**用于和客戶端進行訊息讀寫
測驗代碼如下:
@Test
public void test1() throws IOException {
// 用于與客戶端建立連接
ServerSocketChannel channel = ServerSocketChannel.open();
channel.bind(new InetSocketAddress(8080));
while (true) {//回圈接收請求,分配子執行緒去執行請求
// 用于和客戶端進行訊息讀寫
SocketChannel socketChannel = channel.accept();
handle(socketChannel);
}
}
public void handle(final SocketChannel socketChannel) throws IOException {
// 2.通信
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
ByteBuffer buffer = ByteBuffer.allocate(8192);
while (true) {
try {
buffer.clear();
socketChannel.read(buffer);
// 從buffer 當中讀出來
buffer.flip();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
String message = new String(bytes);
System.out.println(message);
// 寫回去
buffer.rewind();
socketChannel.write(buffer);
if (message.trim().equals("exit")) {
break;
}
} catch (Exception e) {
e.printStackTrace();
}
}
try {
socketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
thread.start();
}
可通過命令進行測驗TCP服務 telnet 127.0.0.1 8080


控制臺輸出了tcp的訊息,并將訊息寫回了管道,命令列界面也輸出了回傳來的hello結果,
6.后續
下一篇博客,介紹NIO中的另一個重要組件Selector
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/160255.html
標籤:其他
上一篇:Mac安裝mysql資料庫,單機
下一篇:攻防世界 Pwn 新手
