NIO,一種基於通道和緩衝區的I/O方式,可使用native函數庫直接分配堆外內存,而後經過一個存儲在javajava
堆的DirectBteBuffer對象做爲這塊內存的引用進行操做,避免了再java堆和native堆中來回複製數據。緩存
NIO是一種同步非阻塞的IO模型。同步是指線程不斷輪詢IO事件是否就緒,非阻塞指線程在等待IO的時候,能夠服務器
同時作其餘任務。同步的核心是Selector,Selector代替了線程自己輪詢IO事件,避免了阻塞同時減小了沒必要要的socket
線程消耗;非阻塞的核心就是通道和緩衝區,當IO事件就緒時,能夠經過寫道緩衝區,保證IO的成功,而無需線程ide
阻塞式地等待。函數
Bufferthis
爲何NIO是基於緩衝區的IO方式,由於當一個鏈接創建完成後,IO數據未必會立刻到達,爲了當數據達到時可以正確
spa
完成IO操做,在BIO(阻塞IO)中,等待IO的線程必須被阻塞,以全天候地進行IO操做。爲了解決這種IO方式的低效問題,.net
引入了緩衝區的概念,當數據達到時,能夠預先被寫入緩衝區,再由緩衝區交給線程,所以線程無需阻塞等待IO線程
通道
當執行:SocketChannel.write(Buffer),將一個buffer寫到一個通道中。通道來講相對比較抽象。通道是I/O傳輸發生時經過的入口
而緩衝區是這些數據傳輸的來源或目標。對於離開緩衝區的傳輸,你想傳遞出去的數據被置於一個緩衝區,被傳送到通道。對於
傳回緩衝區的傳輸,一個通道將數據放置於你所提供的緩衝區中。
例若有個服務器通道ServerSocketChannel serverChannel,一個客戶端通道SocketChannel clientChannel;服務器緩存區
serverBuffer,客戶端緩衝區clientBuffer。當服務器想向客戶端發送數據時,須要調用clientChannel.write(serverBuffer)。當
客戶端要讀時,調用clientChannel.read(clientBuffer);當客戶端向服務器發送數據時,須要調用serverChannel.write(clientBuffer)
當服務器要讀時,調用serverChannel.read(serverBuffer); 能夠理解爲在NIO中,若是想將data發送到目標端,則須要將存儲該
data的buffer,寫入到目標端channel中,而後再從channel中讀取數據到目標端的buffer中。
Selector
通道和緩衝區的機制,使得現場無需阻塞等待IO事件的就緒,但總的要有人來監管這些IO事件。 這就由selector來完成。selector容許
單個現場處理多個channel,若是你的應用打開了多個鏈接(通道),但每一個鏈接的流量都很低,使用selector就比較方便
要使用selector,的向selector註冊channel,而後調用它的select方法,這個方法會一直阻塞到某個註冊的通道有事件就緒,這就是輪詢。
一旦這個方法返回,線程就能夠處理這些事件。
selector中註冊的感興趣事件有: OP_ACCEPT OP_CONNECT OP_READ OP_WRITE
簡單基於NIO方式實現server/client示例
package com.exe.learn.demo.nio; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; public class ServerNio { private Selector selector; public void initServer(int port) throws IOException { ServerSocketChannel serverChannel = ServerSocketChannel.open(); //設置非阻塞模式 serverChannel.configureBlocking(false); serverChannel.socket().bind(new InetSocketAddress(port)); this.selector = Selector.open(); //將selector註冊到服務端 serverChannel.register(selector, SelectionKey.OP_ACCEPT); } public void listen() throws IOException { System.out.println("服務端啓動》》》》》"); while(true) { selector.select(); Iterator<SelectionKey> ite = this.selector.selectedKeys().iterator(); while(ite.hasNext()) { SelectionKey key = (SelectionKey) ite.next(); ite.remove(); if(key.isAcceptable()) { ServerSocketChannel server = (ServerSocketChannel) key.channel(); SocketChannel socketChannel = server.accept(); socketChannel.configureBlocking(false); socketChannel.write(ByteBuffer.wrap(new String("來自服務端的信息").getBytes())); socketChannel.register(this.selector, SelectionKey.OP_READ); }else if(key.isReadable()){ readFromClient(key); } } } } private void readFromClient(SelectionKey key) throws IOException { SocketChannel server = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(10); server.read(buffer); byte[] data = buffer.array(); String msg = new String(data).trim(); System.out.println("來自客戶端的信息" + msg); server.write(ByteBuffer.wrap(new String(msg).getBytes())); } public static void main(String[] args) throws IOException { ServerNio server = new ServerNio(); server.initServer(1024); server.listen(); } }
package com.exe.learn.demo.nio; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.util.Iterator; public class ClientNio { private Selector selector; public void initClient(String ip, int port) throws IOException { SocketChannel socketChannel = SocketChannel.open(); socketChannel.configureBlocking(false); this.selector = Selector.open(); //鏈接server端 socketChannel.connect(new InetSocketAddress(ip, port)); socketChannel.register(selector, SelectionKey.OP_CONNECT); } public void listen() throws IOException { System.out.println("客戶端啓動》》》》》》"); while(true) { selector.select(); Iterator<SelectionKey> ite = this.selector.selectedKeys().iterator(); while(ite.hasNext()) { SelectionKey key = ite.next(); ite.remove(); if(key.isConnectable()) { SocketChannel socketChannel = (SocketChannel) key.channel(); if(socketChannel.isConnectionPending()) { socketChannel.finishConnect(); } socketChannel.configureBlocking(false); socketChannel.write(ByteBuffer.wrap(new String("向服務器發送信息了".getBytes(), "UTF-8").getBytes())); socketChannel.register(selector, SelectionKey.OP_READ); }else if(key.isReadable()) { readFromServer(key); } } } } private void readFromServer(SelectionKey key) throws IOException { SocketChannel server = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(10); server.read(buffer); byte[] data = buffer.array(); String msg = new String(data).trim(); System.out.println("來自服務端的信息" + msg); server.write(ByteBuffer.wrap(new String(msg).getBytes())); } public static void main(String[] args) throws IOException { ClientNio client = new ClientNio(); client.initClient("127.0.0.1", 1024); client.listen(); } }