服務端:java
ServerSocket server = new ServerSocket(1000); Socket conn = server.accept(); InputStream in = conn.getInputStream(); InputStreamReader reader = new BufferedReader(reader); Request request = new Request(); while(!request.isComplete()){ String line = reader.readLine(); request.addLine(line); }
上述操做有兩個問題:linux
BufferedReader默認有$2^{13}$次方字符的緩衝大小。數組
相似的,在處理寫操做時,一次寫入一個字符,效率很低,也須要使用緩衝寫,可是這也會插死你橫更多的垃圾。服務器
在傳統的I/O中要使用大量的線程,一般使用線程池實現來處理請求。 可是即便這樣,仍是會有不少時間阻塞在I/O上,沒有有效的利用CPU網絡
傳統的I/O使用String來操做,浪費資源,新I/O經過使用Buffer讀寫數據避免浪費。app
Buffer對象是線性的,有序的數據集合,他根據其類別只包含惟一的數據類型。異步
java.nio.Buffer
: 類描述java.nio.ByteBuffer
:字節類型。能夠從ReadableByteChannel
中讀,在WritableByteChannel
中寫java.nio.CharBuffer
:字符類型,不能寫入通道java.nio.DoubleBuffer
:double類型,不能寫入通道java.nio.FloatBuffer
:float類型java.nio.IntBuffer
:int類型java.nio.LongBuffer
:long類型java.nio.ShortBuffer
:short類型能夠使用allocate(int capacity)
方法或者allocateDirect(int capacity)
方法分配一個Buffer。socket
特別的,能夠經過調用FileChannel.map(int mode, long position, int size)
建立MappedByteBuffer
。ide
Direct Buffer 在內存中分配一段連續的塊並使用本地訪問方法讀寫數據。non direct Buffer用過java中的數組讀寫數據。函數
有時間必須使用非直接的緩衝,例如使用任何wrap方法(如ButeBuffer.wrap(byte[]))在java數據自出上建立buffer。
向ByteBuffer
中存放數據涉及兩個問題:字節的順序和字符轉換。ByteBuffer
內部經過ByteOrder
類處理了字節順序問題,可是並未解決字符轉換的問題。ByteBuffer沒有提供方法讀寫String。
java.nio.charset.Charset
處理字符轉換的問題。經過構造CharsetEncoder和CharsetDecoder將字符序列轉爲字節和逆轉換。
java.io
類中沒有一個類能夠讀寫Buffer類型,nio提供Channel讀寫Buffer。channel能夠認爲是一種鏈接,能夠使到特定的設備,程序或者是網絡。 channel類的等級結構以下: Channel(interface)->ReadableByteChannel(interface)->ScatteringByteChannel(interface) Channel(interface)->WritableByteChannel(interface)->GatherByteChannel(interface)
ByteChannel(interface)繼承自: ReadableByteChannel(interface) WritableByteChannel(interface)
GatherByteChannel能夠一次將多個Buffer中的數據寫入通道,相反的ScatteringByteChannel能夠一次將數據從通道中讀入多個buffer中。還能夠設置通道使其爲阻塞或非阻塞I/O操做服務。
爲了使通道與傳統I/O兼容,Channel提供了靜態的Stream或Reader。
在過去的阻塞I/O中,咱們通常知道何時能夠向stream中讀或寫,由於方法調用直到stream準備好時返回。可是使用非阻塞通道,咱們須要一些方法來知道何時通道準備好了。在NIO包中,設計Selector就是爲了這個目的。
SelectableChannel
能夠註冊特定的事件,而不是在事件發生時通知應用,通道跟蹤事件。而後,當應用調用Selector上的任意一個selection方法時,它查看註冊了的通道看是否有任何感興趣的事件發生。
並非全部的通道都支持全部的操做。SelectionKey
類定義了全部可能的操做位,將要用兩次。
SelectableChannel.register(Selector sel,int op)
方法註冊通道時,它將所需操做做爲第二個參數傳遞到方法中。SelectionKey
被選中了,SelectionKey
的readyOps()
方法返回全部通道支持操做的位數的和。SelectableChannel
的validOps
方法返回每一個通道容許的操做。註冊通道不支持的操做將引起
IllegalArgumentException
異常.
SelectableChannel
子類支持的操做:
ServerSocketChannel OP_ACCEPT SocketChannel OP_CONNECT, OP_READ, OP_WRITE DatagramChannel OP_READ, OP_WRITE Pipe.SourceChannel OP_READ Pipe.SinkChannel OP_WRITE
import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.nio.charset.Charset; public class WebDownload { private final static Charset charset = Charset.forName("UTF-8"); private SocketChannel clientChannel; public void download() { connect(); sendRequest(); readResponse(); } //發送GET請求到CSDN的文檔中心 private void sendRequest() { //使用channel.write方法,它須要CharByte類型的參數,使用 //Charset.encode(String)方法轉換字符串。 try { clientChannel.write(charset.encode("GET / HTTP/1.1\r\n\r\n")); } catch (IOException e) { e.printStackTrace(); } } private void readResponse(){ ByteBuffer buff = ByteBuffer.allocate(1024);//建立1024字節的緩衝 try { // -1 if the channel has reached end-of-stream while(clientChannel.read(buff)!=-1){ buff.flip();//flip方法在讀緩衝區字節操做以前調用。 System.out.println(charset.decode(buff)); buff.clear(); } } catch (IOException e) { e.printStackTrace(); } } private boolean connect() { InetSocketAddress socketAddr = new InetSocketAddress("www.baidu.com", 80); try { clientChannel = SocketChannel.open(); clientChannel.connect(socketAddr); return true; } catch (IOException e) { e.printStackTrace(); } return false; } public static void main(String[] args) { new WebDownload().download(); } }
server端
import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; public class AddServer { private ServerSocketChannel server = null; private SocketChannel client = null; private ByteBuffer buff = ByteBuffer.allocate(8); private IntBuffer intBuff = buff.asIntBuffer(); public void connect(){ try { server = ServerSocketChannel.open(); server.bind(new InetSocketAddress(80)); System.out.println("channel open!"); } catch (IOException e) { e.printStackTrace(); } } public void waitForConnection(){ try { client = server.accept(); if(client!=null){ System.out.println("client connect!"); processRequest(); } } catch (IOException e) { e.printStackTrace(); } } public void processRequest(){ buff.clear(); try { client.read(buff); int result = intBuff.get(0)+intBuff.get(1); buff.flip(); buff.clear(); intBuff.put(0, result); client.write(buff); } catch (IOException e) { e.printStackTrace(); } } public void run(){ this.connect(); this.waitForConnection(); this.processRequest(); } /** * [@param](http://my.oschina.net/u/2303379) args */ public static void main(String[] args) { new AddServer().run(); } }
client 端
import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.nio.channels.SocketChannel; public class AddClient { private SocketChannel client = null; private ByteBuffer buff = ByteBuffer.allocate(8); private IntBuffer intBuff = buff.asIntBuffer(); public void connect() { try { client = SocketChannel.open(); client.connect(new InetSocketAddress("localhost", 80)); } catch (IOException e) { e.printStackTrace(); } } public void request(int a, int b) { buff.clear(); intBuff.put(0, a); intBuff.put(1, b); try { client.write(buff); System.out.println("send request :" + a + "+" + b); } catch (IOException e) { e.printStackTrace(); } } public int getresult() { buff.clear(); int result = 0; try { client.read(buff); result = buff.getInt(0); } catch (IOException e) { e.printStackTrace(); } finally{ try { client.close(); } catch (IOException e) { e.printStackTrace(); } } return result; } public int start(int a, int b){ this.connect(); this.request(a, b); return this.getresult(); } /** * [@param](http://my.oschina.net/u/2303379) args */ public static void main(String[] args) { System.out.println(new AddClient().start(123, 345)); } }
import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.channels.spi.SelectorProvider; import java.util.Iterator; import java.util.Set; public class AddServer { private ServerSocketChannel server = null; private SocketChannel client = null; private ByteBuffer buff = ByteBuffer.allocate(8); private IntBuffer intBuff = buff.asIntBuffer(); public void connect(){ try { server = ServerSocketChannel.open(); server.bind(new InetSocketAddress(80)); server.configureBlocking(false); System.out.println("channel open!"); } catch (IOException e) { e.printStackTrace(); } } public void waitForConnection(){ Selector acceptSelector; try { acceptSelector = SelectorProvider.provider().openSelector(); SelectionKey acceptKey = server.register(acceptSelector, SelectionKey.OP_ACCEPT); int keyadded = 0; while( (keyadded = acceptSelector.select()) > 0){ Set readyKeys = acceptSelector.selectedKeys(); Iterator it = readyKeys.iterator(); while(it.hasNext()){ SelectionKey sk = (SelectionKey)it.next(); it.remove(); ServerSocketChannel nextReaedy = (ServerSocketChannel)sk.channel(); client = nextReaedy.accept(); processRequest(); } } } catch (IOException e1) { e1.printStackTrace(); } } public void processRequest(){ buff.clear(); try { client.read(buff); int result = intBuff.get(0)+intBuff.get(1); buff.flip(); buff.clear(); intBuff.put(0, result); client.write(buff); } catch (IOException e) { e.printStackTrace(); } } public void run(){ this.connect(); this.waitForConnection(); this.processRequest(); } /** * [@param](http://my.oschina.net/u/2303379) args */ public static void main(String[] args) { new AddServer().run(); } }
非阻塞的加法服務器首先經過SelectorProvider
工廠方法創建選擇器
acceptSelector = SelectorProvider.provider().openSelector();
而後在ServerSocketChannel
上註冊選擇器和對應的事件。
SelectionKey acceptKey = server.register(acceptSelector, SelectionKey.OP_ACCEPT);
經過選擇器獲取當前是否有client鏈接到server:
acceptSelector.select()>0
而後將有client連接到server,獲取成功鏈接的迭代器。遍歷已經準備好的鏈接,分別處理請求。