網絡應用程序一個很重要的工做是傳輸數據。傳輸數據的過程不同取決於使用哪一種「交通工具「,可是傳輸的方式都是同樣的:都是以字節碼傳輸。JAVA開發網絡程序傳輸數據的過程和方式是被抽象了的,咱們不須要關注底層接口,只須要使用Java API 或其餘網絡框架就能達到數據傳輸的目的。發送數據和接收數據都是字節碼。編程
Socket網絡編程我就很少囉嗦了,這裏我經過兩個簡單的示例比較下阻塞式IO(OIO)和非阻塞式IO(NIO)。服務器
OIO中,每一個線程只能處理一個channel,該線程和該channel綁定。也就是同步的,客戶端在發送請求後,必須得在服務端有迴應後才發送下一個請求。因此這個時候的全部請求將會在服務端獲得同步。網絡
NIO中,每一個線程能夠處理多個channel。也就是異步的,客戶端在發送請求後,沒必要等待服務端的迴應就能夠發送下一個請求,這樣對於全部的請求動做來講將會在服務端獲得異步,這條請求的鏈路就象是一個請求隊列,全部的動做在這裏不會獲得同步的。併發
你可能使用過Java提供的網絡接口工做過,遇到過想從阻塞傳輸切換到非阻塞傳輸的狀況,這種狀況是比較困難的,由於阻塞IO和非阻塞IO使用的API有很大的差別。當咱們想切換傳輸方式時要花很大的精力和時間來重構代碼。app
先看一個傳統的阻塞IO傳輸實現的Socket服務端:
框架
1 /** 2 * 傳統阻塞IO(OIO),原始socket 3 * 4 * <p>Title: PlainOioServer</p> 5 * @author wyx 6 * @date 2016-6-15 下午1:36:04 7 */ 8 public class PlainOioServer { 9 public void server(int port) throws Exception{ 10 // bind server to port 11 final ServerSocket socket = new ServerSocket(port); 12 while(true){ 13 // accept connection 14 final Socket clientSocket = socket.accept(); 15 System.out.println("Accepted connection form " + clientSocket); 16 // create new thread to handle connection 17 new Thread(new Runnable() { 18 19 @Override 20 public void run() { 21 OutputStream out; 22 try { 23 out = clientSocket.getOutputStream(); 24 // write message to connected client 25 out.write("Hi!\r\n".getBytes(Charset.forName("UTF-8"))); 26 out.flush(); 27 // close connection once message written and flushed 28 clientSocket.close(); 29 } catch (IOException e) { 30 e.printStackTrace(); 31 } 32 } 33 }).start(); // start thread to begin handling 34 } 35 } 36 }
上面的方式很簡潔,可是這種阻塞模式在大鏈接的狀況就會有嚴重的問題,如:客戶端鏈接超時,服務器響應嚴重延遲等。爲了解決這一問題,咱們可使用異步網絡處理全部的併發鏈接,但問題在於NIO和OIO的API是徹底不一樣的,因此一個用OIO開發的網絡應用程序想要使用NIO重構代碼幾乎是從新開發。異步
下面代碼是使用Java NIO實現的例子:socket
1 /** 2 * 傳統非阻塞式IO(NIO),原始socket 3 * 4 * <p>Title: PlainNioServer</p> 5 * @author wyx 6 * @date 2016-6-15 下午1:46:09 7 */ 8 public class PlainNioServer { 9 public void server(int port) throws Exception{ 10 System.out.println("Listening for connections on port " + port); 11 // open selector that handles channels 12 Selector selector = Selector.open(); 13 // open ServerSocketChannel 14 ServerSocketChannel serverChannel = ServerSocketChannel.open(); 15 // get ServerSocket 16 ServerSocket serverSocket = serverChannel.socket(); 17 // bind server to port 18 serverSocket.bind(new InetSocketAddress(port)); 19 // set to non-blocking 20 serverChannel.configureBlocking(false); 21 // register ServerSocket to selector and specify than it is interested in new accepted clients 22 serverChannel.register(selector, SelectionKey.OP_ACCEPT); 23 final ByteBuffer msg = ByteBuffer.wrap("Hi!\r\n".getBytes()); 24 while(true){ 25 // Wait for new events that are ready for process. this will block until something happens 26 int n = selector.select(); 27 if(n > 0){ 28 // Obtain all SelectionKey instances that received enents 29 Iterator<SelectionKey> iter = selector.selectedKeys().iterator(); 30 while(iter.hasNext()){ 31 SelectionKey key = iter.next(); 32 iter.remove(); 33 //Check if event was because new client ready to get accepted 34 if(key.isAcceptable()){ 35 ServerSocketChannel server = (ServerSocketChannel) key.channel(); 36 SocketChannel client = server.accept(); 37 System.out.println("Accepted connection from " + client); 38 client.configureBlocking(false); 39 // Accept client and register it to seletor 40 client.register(selector, SelectionKey.OP_WRITE, msg.duplicate()); 41 } 42 43 // Check if event was because socket is ready to write data 44 if(key.isWritable()){ 45 SocketChannel client = (SocketChannel) key.channel(); 46 ByteBuffer buff = (ByteBuffer) key.attachment(); 47 // Write date to connected client 48 while(buff.hasRemaining()){ 49 if(client.write(buff) == 0){ 50 break; 51 } 52 } 53 client.close(); 54 } 55 } 56 } 57 } 58 } 59 }
如你所見,即便它們實現的功能時候同樣的,可是代碼徹底不一樣。根據不一樣需求選用不一樣的實現方式,固然,也能夠直接選擇流行的網絡傳輸框架實現,如:Netty。以便於後期維護。ide