bio | nio |
---|---|
單向的(流) | 雙向的(通道) |
面向流的 | 面向緩衝區 |
阻塞 | 非阻塞 |
無 | 選擇器 |
對於我們之前所學的bio就是通過流的io操作他總是在每次要進行數據流動時建立一個輸入流一個輸出流而且這個流在沒有數據的時候就會阻塞程序,也就是說會使得程序代碼無法進行下去我們可以通過前端頁面中的異步來理解。在前端異步通信發生時前端的代碼並不會應爲這個異步通訊還沒有結束就一直等在那裏不向下進行。而是跳過這個異步執行下面的代碼當異步執行完畢有了返回時就跳回到異步那邊再去執行。而流就恰恰相反如果這個流傳輸一天都不能停,那麼這一天以內流下面哪一行代碼就沒得執行。這一點我們可以通過虛擬機棧執行方法來理解一下,虛擬機棧執行一個方法會在棧中開闢一個棧幀存儲本地變量和操作數,而方法具體的執行步驟是要到方法區中的類信息中去找並嚴格按照類信息中的執行步驟向下走,當執行到了流那就等着這個執行結束纔會繼續向下走。
nio則是爲了解決這個問題而產的新io操作api它不僅不在是單純的依靠流並且將這個nio放入了新的java.nio類中不是之前的io着可以看出他們之間一定是有所區別的新io不同於之前的io他是通過建立一個channel並搭配着buffer區來進行數據通信的,只需要一個管道channel並將數據放於buffer中再通過buffer來傳輸數據
緩衝區的數據存區
import java.nio.ByteBuffer;
/**
* buffer緩衝區負責在Javaio中進行數據的存取,緩衝區就是各種數據類型數組
* 根據數據的類型不同提供了不同類型的緩衝區(Boolean除外其他的基本類型)
*
*存取數據的核心方法
* put get
*
* 緩衝區的核心屬性
* // 這四個屬性是繼承自buffer類的是所有的buffer都具備的屬性他設定了buffer的基本屬性
* private int mark = -1; 書籤位置,記錄的一個位置可以直接將position回到這裏
* private int position = 0; 當前所在位置,正在操做的位置
*
* private int limit; 限制表示緩衝區中可以操作數據的大小
* 也就是limit後面的數據是無法操作的
* private int capacity; 容量我們通過allocate設置的就是這
* 個緩衝區的capacity大小這個屬性是無法改變的應爲底層畢竟是數組
*/
public class testt {
public static void main(String[] args) {
//1.分配一個指定大小的緩衝區 position在底部隨着加入會不斷向上
//limit在最頂部
ByteBuffer buffer = ByteBuffer.allocate(1024);
//2。存數據
System.out.println(String.format("position: %s limit: %s",buffer.position(),buffer.limit()));
buffer.put((byte) 11);
buffer.put("abcde".getBytes());
System.out.println(String.format("position: %s limit: %s",buffer.position(),buffer.limit()));
//切換到讀數據的狀態
//3.取數據
buffer.flip();
//當執行了這個方法時position就會回到數據組的底部,並且limit會回到緩衝區存儲的數據量的位置
//將mark設置爲這裏
buffer.mark();
System.out.println( buffer.get());
System.out.println(String.format("position: %s limit: %s",buffer.position(),buffer.limit()));
//4.繼續重頭讀
//通過方法回到讀模式
buffer.rewind();
System.out.println(String.format("position: %s limit: %s",buffer.position(),buffer.limit()));
//假清空,只是將幾個屬性重置了
buffer.clear();
System.out.println(String.format("position: %s limit: %s",buffer.position(),buffer.limit()));
}
}
直接緩衝區和非直接緩衝區
也可以說時堆內緩衝區和堆外緩衝區
allocate設置的是將緩衝區建立在堆中也就是jvm的內存中而ByteBuffer.allocateDirect()這個方法是將緩衝區建立在內存中是在堆外可以增加效率但是需要我們自己記得關閉和釋放這些內存
通道java.nio.channel
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-L1n2mvt2-1587285891297)(F:\opensource\學習積累\學習筆記\java進階學習\java高級課程學習筆記\image\通道內存圖.PNG)]
import sun.dc.pr.PathStroker;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
/**
* 通道channel用於源節點與目標節點的一個連接
* 在nio中負責緩衝區中數據的傳輸,channel本身是不存儲數據的要配合緩衝區完成傳輸
* 主要實現類
* java.nio.channel接口:
* 1.filechannel 文件傳輸用於本地文件的傳輸
* 2.sockerchannel 網絡傳輸 tcp
* 3.serversockerchannel 網絡傳輸 tcp
* 4.datagramchannel 網絡傳輸 udp
*
* 獲取通道
* 1. getchannel()方法獲取通道
* 2.jdk1.7中nio2提供了靜態方法針對各個通道提供了open(方法
* 3.jdk1.7 nio2的一個FILES工具類的newByteChannel方法
*
* 通道之間的數據傳輸
* transferfrom()
* transferto()
*
*/
public class testt {
public static void main(String[] args) throws IOException {
//1方法
// FileInputStream fileInputStream = new FileInputStream("F:\\新建文件夾\\test.PNG");
// FileOutputStream fileOutputStream = new FileOutputStream("F:\\新建文件夾\\test2.PNG");
// FileChannel channel = fileInputStream.getChannel();
// FileChannel channel1 = fileOutputStream.getChannel();
// ByteBuffer buf = ByteBuffer.allocate(1024);
//
// while (channel.read(buf)!=-1){
// buf.flip();
// channel1.write(buf);
// buf.clear();
// }
// channel.close();
// channel1.close();
// fileInputStream.close();
// fileOutputStream.close();
//2方法
// FileChannel open = FileChannel.open(Paths.get("F:\\新建文件夾\\test.PNG"), StandardOpenOption.READ);
// FileChannel fileChannel = FileChannel.open(Paths.get("F:\\新建文件夾\\test2.PNG"), StandardOpenOption.WRITE,StandardOpenOption.READ);
// //直接在通道中傳輸
// open.transferTo(0,open.size(),fileChannel);
// open.close();
// fileChannel.close();
}
}
分散與聚集
- 分散讀取:將通道中的數據分散到多個緩衝區
- 聚集寫入:將多個緩衝區中的數據聚集到一個通道中
import sun.dc.pr.PathStroker;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
/**
* 分散與聚集
* 分散讀取(scattering Readers):將通道中的數據分散到多個緩衝區
* 聚集寫入(gathering Writes):將多個緩衝區中的數據聚集到一個通道中
*
*/
public class testt {
public static void main(String[] args) throws IOException {
//分散讀取
FileInputStream inputStream = new FileInputStream("");
FileChannel channel = inputStream.getChannel();
ByteBuffer allocate = ByteBuffer.allocate(100);
ByteBuffer allocate1 = ByteBuffer.allocate(1024);
ByteBuffer[] buffers = {allocate,allocate1};
channel.read(buffers);
channel.close();
inputStream.close();
//聚集寫入同理
}
}
字符集
編碼
解碼
public class testt {
public static void main(String[] args) throws IOException {
SortedMap<String, Charset> stringCharsetSortedMap =
Charset.availableCharsets();
Set<Map.Entry<String, Charset>> entries =
stringCharsetSortedMap.entrySet();
Iterator<Map.Entry<String, Charset>> iterator = entries.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
Charset charset = Charset.forName("UTF-8");
CharsetEncoder charsetEncoder = charset.newEncoder();
CharsetDecoder charsetDecoder = charset.newDecoder();
CharBuffer buffer = CharBuffer.allocate(1024);
buffer.put("tests");
buffer.flip();
charsetEncoder.encode(buffer);
nio阻塞式與非阻塞網絡通信
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-CPwNOTLU-1587285891299)(F:\opensource\學習積累\學習筆記\java進階學習\java高級課程學習筆記\image\nio非阻塞模式.PNG)]
nio的阻塞式
import org.junit.Test;
import sun.dc.pr.PathStroker;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
/**
*使用nio完成網絡通信的核心
* 1。通道channel:負責鏈接
* ----socketChannel
* ----serverSocketChannel
* ----DatagramChannel
*
* ----Pipe.SinkChannel
* ----Pipe.SourceChannel
* 2.緩衝區buffer:負責數據的存取
* 3.選擇器selector:用於監控被註冊的通道的狀況
*/
public class testt {
@Test
public void client() throws IOException {
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",9001));
ByteBuffer buffer = ByteBuffer.allocate(1024);
//讀取本地文件併發送
FileChannel open = FileChannel.open(Paths.get("F:\\新建文件夾\\test.PNG"), StandardOpenOption.READ);
while (open.read(buffer)!=-1){
buffer.flip();
socketChannel.write(buffer);
buffer.clear();
}
//關閉本地文件通道
open.close();
socketChannel.close();
}
@Test
public void Server() throws IOException {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//綁定端口號
serverSocketChannel.bind(new InetSocketAddress(9001));
//獲取客戶端的通道
SocketChannel socketChannel = serverSocketChannel.accept();
//讀取數據並保存
ByteBuffer buffer = ByteBuffer.allocate(1024);
FileChannel open = FileChannel.open(Paths.get("F:\\新建文件夾\\test2.PNG"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
while (socketChannel.read(buffer)!=-1){
buffer.flip();
open.write(buffer);
buffer.clear();
}
open.close();
socketChannel.close();
serverSocketChannel.close();
}
}
非阻塞式
import org.junit.Test;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Date;
import java.util.Iterator;
/**
*使用nio完成網絡通信的核心
* 1。通道channel:負責鏈接
* ----socketChannel
* ----serverSocketChannel
* ----DatagramChannel
*
* ----Pipe.SinkChannel
* ----Pipe.SourceChannel
* 2.緩衝區buffer:負責數據的存取
* 3.選擇器selector:用於監控被註冊的通道的狀況
*/
public class testt2 {
@Test
public void client() throws IOException {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("127.0.0.1",9001));
//切換爲非阻塞模式
socketChannel.configureBlocking(false);
//緩衝區
ByteBuffer buffer = ByteBuffer.allocate(1024);
//發送數據給服務端
buffer.put(new Date().toString().getBytes());
buffer.flip();
socketChannel.write(buffer);
buffer.clear();
//關閉
socketChannel.close();
}
@Test
public void Server() throws IOException {
//獲取客戶端通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//設置非阻塞
serverSocketChannel.configureBlocking(false);
//綁定端口號
serverSocketChannel.bind(new InetSocketAddress(9001));
//獲取選擇器
Selector selector = Selector.open();
//將通道註冊到選擇器,並指定監聽事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
//通過選擇器輪詢獲取選擇器上以及準備就緒的事件
while (selector.select()>0){
//獲取所有以及滿足上面設置的狀態的註冊事件
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
//獲取準備就緒的事件
if (selectionKey.isAcceptable()){
SocketChannel socketChannel = serverSocketChannel.accept();
//切換非阻塞模式
socketChannel.configureBlocking(false);
//將該通道註冊到選擇器
socketChannel.register(selector,SelectionKey.OP_READ);
}else if (selectionKey.isReadable()){
//獲取都就緒狀態的通道
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
//讀取數據
ByteBuffer buffer = ByteBuffer.allocate(1024);
int len = 0;
while ((len = socketChannel.read(buffer))>0){
buffer.flip();
System.out.println(new String(buffer.array(),0,len));
buffer.clear();
}
}
// //取消選擇鍵
// iterator.remove();
}
}
serverSocketChannel.close();
}
}
int len = 0;
while ((len = socketChannel.read(buffer))>0){
buffer.flip();
System.out.println(new String(buffer.array(),0,len));
buffer.clear();
}
}
// //取消選擇鍵
// iterator.remove();
}
}
serverSocketChannel.close();
}
}