NIO和IO的區別:java
IO是面向流的,NIO是面向緩衝區的。服務器
Java IO的各類流是阻塞的。這意味着,當一個線程調用read() 或 write()時,該線程被阻塞,直到有一些 數 據被讀取,或數據徹底寫入。java NIO的非阻塞模式,使一個線程從某通道發送請求讀取數據,可是它 僅能獲得目前可用的數據,若是目前沒有數據可用時,就什麼都不會獲取,不是保持線程阻塞。
socket
Java NIO的選擇器容許一個單獨的線程來監視多個輸入通道。測試
若是須要管理同時打開的成千上萬個鏈接,這些鏈接每次只是發送少許的數據,例如聊天服務器,實現NIO的服務器多是一個優點。this
服務端實例:spa
public class NIOServer { //通道管理器 private Selector selector; /* 得到一個ServerSocket通道,並對該通道作一些初始化的工做*/ public void initServer(int port) throws IOException { // 得到一個ServerSocket通道 ServerSocketChannel serverChannel = ServerSocketChannel.open(); // 設置通道爲非阻塞 serverChannel.configureBlocking(false); // 將該通道對應的ServerSocket綁定到port端口 serverChannel.socket().bind(new InetSocketAddress(port)); // 得到一個通道管理器 this.selector = Selector.open(); //將通道管理器和該通道綁定,併爲該通道註冊SelectionKey.OP_ACCEPT事件,註冊該事件後, //當該事件到達時,selector.select()會返回,若是該事件沒到達selector.select()會一直阻塞。 serverChannel.register(selector, SelectionKey.OP_ACCEPT); } /** * 採用輪詢的方式監聽selector上是否有須要處理的事件,若是有,則進行處理 * @throws IOException */ @SuppressWarnings("unchecked") public void listen() throws IOException { System.out.println("服務端啓動成功!"); // 輪詢訪問selector while (true) { //當註冊的事件到達時,方法返回;不然,該方法會一直阻塞 selector.select(); // 得到selector中選中的項的迭代器,選中的項爲註冊的事件 Iterator ite = this.selector.selectedKeys().iterator(); while (ite.hasNext()) { SelectionKey key = (SelectionKey) ite.next(); // 刪除已選的key,以防重複處理 ite.remove(); // 客戶端請求鏈接事件 if (key.isAcceptable()) { ServerSocketChannel server = (ServerSocketChannel) key .channel(); // 得到和客戶端鏈接的通道 SocketChannel channel = server.accept(); // 設置成非阻塞 channel.configureBlocking(false); //在這裏能夠給客戶端發送信息哦 channel.write(ByteBuffer.wrap(new String("向客戶端發送了一條信息").getBytes())); //在和客戶端鏈接成功以後,爲了能夠接收到客戶端的信息,須要給通道設置讀的權限。 channel.register(this.selector, SelectionKey.OP_READ); // 得到了可讀的事件 } else if (key.isReadable()) { read(key); } } } } /** * 處理讀取客戶端發來的信息 的事件 * @param key * @throws IOException */ public void read(SelectionKey key) throws IOException{ // 服務器可讀取消息:獲得事件發生的Socket通道 SocketChannel channel = (SocketChannel) key.channel(); // 建立讀取的緩衝區 ByteBuffer buffer = ByteBuffer.allocate(10); channel.read(buffer); byte[] data = buffer.array(); String msg = new String(data).trim(); System.out.println("服務端收到信息:"+msg); ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes()); channel.write(outBuffer);// 將消息回送給客戶端 } /** * 啓動服務端測試 * @throws IOException */ public static void main(String[] args) throws IOException { NIOServer server = new NIOServer(); server.initServer(8000); server.listen(); } }
客戶端實例.net
package com.ailk.fba.interfacefeel; 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; /** * NIO客戶端 * @author 小路 */ public class NIOClient { //通道管理器 private Selector selector; /** * 得到一個Socket通道,並對該通道作一些初始化的工做 * @param ip 鏈接的服務器的ip * @param port 鏈接的服務器的端口號 * @throws IOException */ public void initClient(String ip,int port) throws IOException { // 得到一個Socket通道 SocketChannel channel = SocketChannel.open(); // 設置通道爲非阻塞 channel.configureBlocking(false); // 得到一個通道管理器 this.selector = Selector.open(); // 客戶端鏈接服務器,其實方法執行並無實現鏈接,須要在listen()方法中調 //用channel.finishConnect();才能完成鏈接 channel.connect(new InetSocketAddress(ip,port)); //將通道管理器和該通道綁定,併爲該通道註冊SelectionKey.OP_CONNECT事件。 channel.register(selector, SelectionKey.OP_CONNECT); } /** * 採用輪詢的方式監聽selector上是否有須要處理的事件,若是有,則進行處理 * @throws IOException */ @SuppressWarnings("unchecked") public void listen() throws IOException { // 輪詢訪問selector while (true) { selector.select(); // 得到selector中選中的項的迭代器 Iterator ite = this.selector.selectedKeys().iterator(); while (ite.hasNext()) { SelectionKey key = (SelectionKey) ite.next(); // 刪除已選的key,以防重複處理 ite.remove(); // 鏈接事件發生 if (key.isConnectable()) { SocketChannel channel = (SocketChannel) key .channel(); // 若是正在鏈接,則完成鏈接 if(channel.isConnectionPending()){ channel.finishConnect(); } // 設置成非阻塞 channel.configureBlocking(false); //在這裏能夠給服務端發送信息哦 channel.write(ByteBuffer.wrap(new String("向服務端發送了一條信息").getBytes())); //在和服務端鏈接成功以後,爲了能夠接收到服務端的信息,須要給通道設置讀的權限。 channel.register(this.selector, SelectionKey.OP_READ); // 得到了可讀的事件 } else if (key.isReadable()) { read(key); } } } } /** * 處理讀取服務端發來的信息 的事件 * @param key * @throws IOException */ public void read(SelectionKey key) throws IOException{ //和服務端的read方法同樣 } /** * 啓動客戶端測試 * @throws IOException */ public static void main(String[] args) throws IOException { NIOClient client = new NIOClient(); client.initClient("localhost",8000); client.listen(); } }