【转】NIO的定义和原理是什么?
NIO和IO到底有什么區別?有什么關系?
?
首先說一下核心區別:
?
NIO是以塊的方式處理數據,但是IO是以最基礎的字節流的形式去寫入和讀出的。所以在效率上的話,肯定是NIO效率比IO效率會高出很多。
NIO不在是和IO一樣用OutputStream和InputStream 輸入流的形式來進行處理數據的,但是又是基于這種流的形式,而是采用了通道和緩沖區的形式來進行處理數據的。
還有一點就是NIO的通道是可以雙向的,但是IO中的流只能是單向的。
還有就是NIO的緩沖區(其實也就是一個字節數組)還可以進行分片,可以建立只讀緩沖區、直接緩沖區和間接緩沖區,只讀緩沖區很明顯就是字面意思,直接緩沖區是為加快 I/O 速度,而以一種特殊的方式分配其內存的緩沖區。
先了解一下什么是通道,什么是緩沖區的概念
?
通道是個什么意思?
?
通道是對原 I/O 包中的流的模擬。到任何目的地(或來自任何地方)的所有數據都必須通過一個 Channel 對象(通道)。一個 Buffer 實質上是一個容器對象。發送給一個通道的所有對象都必須首先放到緩沖區中;同樣地,從通道中讀取的任何數據都要讀到緩沖區中。 Channel是一個對象,可以通過它讀取和寫入數據。拿 NIO 與原來的 I/O 做個比較,通道就像是流。
正如前面提到的,所有數據都通過 Buffer 對象來處理。您永遠不會將字節直接寫入通道中,相反,您是將數據寫入包含一個或者多個字節的緩沖區。同樣,您不會直接從通道中讀取字節,而是將數據從通道讀入緩沖區,再從緩沖區獲取這個字節。
緩沖區是什么意思:
?
Buffer 是一個對象, 它包含一些要寫入或者剛讀出的數據。 在 NIO 中加入 Buffer 對象,體現了新庫與原 I/O 的一個重要區別。在面向流的 I/O 中,您將數據直接寫入或者將數據直接讀到 Stream 對象中
在 NIO 庫中,所有數據都是用緩沖區處理的。在讀取數據時,它是直接讀到緩沖區中的。在寫入數據時,它是寫入到緩沖區中的。任何時候訪問 NIO 中的數據,您都是將它放到緩沖區中。
緩沖區實質上是一個數組。通常它是一個字節數組,但是也可以使用其他種類的數組。但是一個緩沖區不 僅僅 是一個數組。緩沖區提供了對數據的結構化訪問,而且還可以跟蹤系統的讀/寫進程
緩沖區的類型:
ByteBuffer
CharBuffer
ShortBuffer
IntBuffer
LongBuffer
FloatBuffer
DoubleBuffer
?
NIO的底層工作原理
?
先來了解一下buffer的工作機制:
?
capacity 緩沖區數組的總長度
position 下一個要操作的數據元素的位置
limit 緩沖區數組中不可操作的下一個元素的位置,limit<=capacity
mark 用于記錄當前 position 的前一個位置或者默認是 0
?
1.這一步其實是當我們剛開始初始化這個buffer數組的時候,開始默認是這樣的
?
?
?
2、但是當你往buffer數組中開始寫入的時候幾個字節的時候就會變成下面的圖,position會移動你數據的結束的下一個位置,這個時候你需要把buffer中的數據寫到channel管道中,所以此時我們就需要用這個buffer.flip();方法,?
3、當你調用完2中的方法時,這個時候就會變成下面的圖了,這樣的話其實就可以知道你剛剛寫到buffer中的數據是在position—->limit之間,然后下一步調用clear();
?4、這時底層操作系統就可以從緩沖區中正確讀取這 5 個字節數據發送出去了。在下一次寫數據之前我們在調一下 clear() 方法。緩沖區的索引狀態又回到初始位置。(其實這一步有點像IO中的把轉運字節數組 char[] buf = new char[1024]; 不足1024字節的部分給強制刷新出去的意思)
?
補充:
?
1、這里還要說明一下 mark,當我們調用 mark() 時,它將記錄當前 position 的前一個位置,當我們調用 reset 時,position 將恢復 mark 記錄下來的值
?
2. clear()方法會:清空整個緩沖區。position將被設回0,limit被設置成 capacity的值(這個個人的理解就是當你在flip()方法的基礎上已經記住你寫入了多少字節數據,直接把position到limit之間的也就是你寫入已經記住的數據給“復制”到管道中)
?
3 . 當你把緩沖區的數局寫入到管道中的時候,你需要調用flip()方法將Buffer從寫模式切換到讀模式,調用flip()方法會將position設回0,并將limit設置成之前position的值。buf.flip();(其實我個人理解的就相當于先記住緩沖區緩沖了多少數據)
?
NIO其實就是主要利用緩沖區來進行傳輸字節:
(客戶端和服務端又是怎么進行通信和傳輸的,以后再來更新)
NIO 工作代碼示例
public void selector() throws IOException {
//先給緩沖區申請內存空間
??????? ByteBuffer buffer = ByteBuffer.allocate(1024);
???? //打開Selector為了它可以輪詢每個 Channel 的狀態
??????? Selector selector = Selector.open();
??????? ServerSocketChannel ssc = ServerSocketChannel.open();
??????? ssc.configureBlocking(false);//設置為非阻塞方式
??????? ssc.socket().bind(new InetSocketAddress(8080));
??????? ssc.register(selector, SelectionKey.OP_ACCEPT);//注冊監聽的事件
??????? while (true) {
??????????? Set selectedKeys = selector.selectedKeys();//取得所有key集合
??????????? Iterator it = selectedKeys.iterator();
??????????? while (it.hasNext()) {
?????????????? ?SelectionKey key = (SelectionKey) it.next();
??????????????? if ((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) {
??????????????????? ServerSocketChannel ssChannel = (ServerSocketChannel) key.channel();
???????????????? SocketChannel sc = ssChannel.accept();//接受到服務端的請求
??????????????????? sc.configureBlocking(false);
??????????????????? sc.register(selector, SelectionKey.OP_READ);
??????????????????? it.remove();
??????????????? } else if
??????????????? ((key.readyOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
??????????????????? SocketChannel sc = (SocketChannel) key.channel();
??????????????????? while (true) {
??????????????????????? buffer.clear();
??????????????????????? int n = sc.read(buffer);//讀取數據
???????????????? ???????if (n <= 0) {
??????????????????????????? break;
??????????????????????? }
??????????????????????? buffer.flip();
??????????????????? }
??????????????????? it.remove();
??????????????? }
??????????? }
??????? }
}
最后給大家看一下整體的NIO的示意圖
?
?
轉載于:https://www.cnblogs.com/fanblogs/p/11039269.html
總結
以上是生活随笔為你收集整理的【转】NIO的定义和原理是什么?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: UI基本原则
- 下一篇: Control.BeginInvoke