IO、NIO、AIO理解

摘要: 關於BIO和NIO的理解html

最近大概看了ZooKeeper和Mina的源碼發現都是用Java NIO實現的,因此有必要搞清楚什麼是NIO。下面是我結合網絡資料本身總結的,爲了節約時間圖示隨便畫的,能達意就行。java

 

簡介:編程

BIO:同步阻塞式IO,服務器實現模式爲一個鏈接一個線程,即客戶端有鏈接請求時服務器端就須要啓動一個線程進行處理,若是這個鏈接不作任何事情會形成沒必要要的線程開銷,固然能夠經過線程池機制改善。 
NIO:同步非阻塞式IO,服務器實現模式爲一個請求一個線程,即客戶端發送的鏈接請求都會註冊到多路複用器上,多路複用器輪詢到鏈接有I/O請求時才啓動一個線程進行處理。 
AIO(NIO.2):異步非阻塞式IO,服務器實現模式爲一個有效請求一個線程,客戶端的I/O請求都是由OS先完成了再通知服務器應用去啓動線程進行處理。 

BIO 
同步阻塞式IO,相信每個學習過操做系統網絡編程或者任何語言的網絡編程的人都很熟悉,在while循環中服務端會調用accept方法等待接收客戶端的鏈接請求,一旦接收到一個鏈接請求,就能夠創建通訊套接字在這個通訊套接字上進行讀寫操做,此時不能再接收其餘客戶端鏈接請求,只能等待同當前鏈接的客戶端的操做執行完成。 
若是BIO要可以同時處理多個客戶端請求,就必須使用多線程,即每次accept阻塞等待來自客戶端請求,一旦受到鏈接請求就創建通訊套接字同時開啓一個新的線程來處理這個套接字的數據讀寫請求,而後馬上又繼續accept等待其餘客戶端鏈接請求,即爲每個客戶端鏈接請求都建立一個線程來單獨處理,大概原理圖就像這樣: 
 

雖然此時服務器具有了高併發能力,即可以同時處理多個客戶端請求了,可是卻帶來了一個問題,隨着開啓的線程數目增多,將會消耗過多的內存資源,致使服務器變慢甚至崩潰,NIO能夠必定程度解決這個問題。服務器


NIO 
同步非阻塞式IO,關鍵是採用了事件驅動的思想來實現了一個多路轉換器。 
NIO與BIO最大的區別就是隻須要開啓一個線程就能夠處理來自多個客戶端的IO事件,這是怎麼作到的呢? 
就是多路複用器,能夠監聽來自多個客戶端的IO事件: 
A. 若服務端監聽到客戶端鏈接請求,便爲其創建通訊套接字(java中就是通道),而後返回繼續監聽,若同時有多個客戶端鏈接請求到來也能夠所有收到,依次爲它們都創建通訊套接字。 
B. 若服務端監聽到來自已經建立了通訊套接字的客戶端發送來的數據,就會調用對應接口處理接收到的數據,若同時有多個客戶端發來數據也能夠依次進行處理。 
C. 監聽多個客戶端的鏈接請求和接收數據請求同時還能監聽本身時候有數據要發送。 
 

總之就是在一個線程中就能夠調用多路複用接口(java中是select)阻塞同時監聽來自多個客戶端的IO請求,一旦有收到IO請求就調用對應函數處理。 

各自應用場景 

到這裏你也許已經發現,一旦有請求到來(不論是幾個同時到仍是隻有一個到),都會調用對應IO處理函數處理,因此:網絡

(1)NIO適合處理鏈接數目特別多,可是鏈接比較短(輕操做)的場景,Jetty,Mina,ZooKeeper等都是基於java nio實現。多線程

(2)BIO方式適用於鏈接數目比較小且固定的場景,這種方式對服務器資源要求比較高,併發侷限於應用中。併發

(3)AIO新的IO2.0,即NIO2.0,jdk1.7開始應用,叫作異步不阻塞的IO。AIO引入異常通道的概念,採用了Proactor模式,簡化了程序編寫,一個有效的請求才啓動一個線程,它的特色是先由操做系統完成後才通知服務端程序啓動線程去處理,通常適用於鏈接數較多且鏈接時間長的應用。異步


附錄:下面附上一個別人寫的java NIO的例子。 
服務端:  
  1. 1. package cn.nio;    
  2. 2.    
  3. 3. import java.io.IOException;    
  4. 4. import java.net.InetSocketAddress;    
  5. 5. import java.nio.ByteBuffer;    
  6. 6. import java.nio.channels.SelectionKey;    
  7. 7. import java.nio.channels.Selector;    
  8. 8. import java.nio.channels.ServerSocketChannel;    
  9. 9. import java.nio.channels.SocketChannel;    
  10. 10. import java.util.Iterator;    
  11. 11.    
  12. 12. /**  
  13. 13. * NIO服務端  
  14. 14. *  
  15. 15. */    
  16. 16. public class NIOServer {    
  17. 17.    //通道管理器    
  18. 18.    private Selector selector;    
  19. 19.    
  20. 20.    /**  
  21. 21.     * 得到一個ServerSocket通道,並對該通道作一些初始化的工做  
  22. 22.     * @param port  綁定的端口號  
  23. 23.     * @throws IOException  
  24. 24.     */    
  25. 25.    public void initServer(int port) throws IOException {    
  26. 26.        // 得到一個ServerSocket通道    
  27. 27.        ServerSocketChannel serverChannel = ServerSocketChannel.open();    
  28. 28.        // 設置通道爲非阻塞    
  29. 29.        serverChannel.configureBlocking(false);    
  30. 30.        // 將該通道對應的ServerSocket綁定到port端口    
  31. 31.        serverChannel.socket().bind(new InetSocketAddress(port));    
  32. 32.        // 得到一個通道管理器    
  33. 33.        this.selector = Selector.open();    
  34. 34.        //將通道管理器和該通道綁定,併爲該通道註冊SelectionKey.OP_ACCEPT事件,註冊該事件後,    
  35. 35.        //當該事件到達時,selector.select()會返回,若是該事件沒到達selector.select()會一直阻塞。    
  36. 36.        serverChannel.register(selector, SelectionKey.OP_ACCEPT);    
  37. 37.    }    
  38. 38.    
  39. 39.    /**  
  40. 40.     * 採用輪詢的方式監聽selector上是否有須要處理的事件,若是有,則進行處理  
  41. 41.     * @throws IOException  
  42. 42.     */    
  43. 43.    @SuppressWarnings("unchecked")    
  44. 44.    public void listen() throws IOException {    
  45. 45.        System.out.println("服務端啓動成功!");    
  46. 46.        // 輪詢訪問selector    
  47. 47.        while (true) {    
  48. 48.            //當註冊的事件到達時,方法返回;不然,該方法會一直阻塞    
  49. 49.            selector.select();    
  50. 50.            // 得到selector中選中的項的迭代器,選中的項爲註冊的事件    
  51. 51.            Iterator ite = this.selector.selectedKeys().iterator();    
  52. 52.            while (ite.hasNext()) {    
  53. 53.                SelectionKey key = (SelectionKey) ite.next();    
  54. 54.                // 刪除已選的key,以防重複處理    
  55. 55.                ite.remove();    
  56. 56.                // 客戶端請求鏈接事件    
  57. 57.                if (key.isAcceptable()) {    
  58. 58.                    ServerSocketChannel server = (ServerSocketChannel) key    
  59. 59.                            .channel();    
  60. 60.                    // 得到和客戶端鏈接的通道    
  61. 61.                    SocketChannel channel = server.accept();    
  62. 62.                    // 設置成非阻塞    
  63. 63.                    channel.configureBlocking(false);    
  64. 64.    
  65. 65.                    //在這裏能夠給客戶端發送信息哦    
  66. 66.                    channel.write(ByteBuffer.wrap(new String("向客戶端發送了一條信息").getBytes()));    
  67. 67.                    //在和客戶端鏈接成功以後,爲了能夠接收到客戶端的信息,須要給通道設置讀的權限。    
  68. 68.                    channel.register(this.selector, SelectionKey.OP_READ);    
  69. 69.                        
  70. 70.                    // 得到了可讀的事件    
  71. 71.                } else if (key.isReadable()) {    
  72. 72.                        read(key);    
  73. 73.                }    
  74. 74.    
  75. 75.            }    
  76. 76.    
  77. 77.        }    
  78. 78.    }    
  79. 79.    /**  
  80. 80.     * 處理讀取客戶端發來的信息 的事件  
  81. 81.     * @param key  
  82. 82.     * @throws IOException   
  83. 83.     */    
  84. 84.    public void read(SelectionKey key) throws IOException{    
  85. 85.        // 服務器可讀取消息:獲得事件發生的Socket通道    
  86. 86.        SocketChannel channel = (SocketChannel) key.channel();    
  87. 87.        // 建立讀取的緩衝區    
  88. 88.        ByteBuffer buffer = ByteBuffer.allocate(10);    
  89. 89.        channel.read(buffer);    
  90. 90.        byte[] data = buffer.array();    
  91. 91.        String msg = new String(data).trim();    
  92. 92.        System.out.println("服務端收到信息:"+msg);    
  93. 93.        ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());    
  94. 94.        channel.write(outBuffer);// 將消息回送給客戶端    
  95. 95.    }    
  96. 96.        
  97. 97.    /**  
  98. 98.     * 啓動服務端測試  
  99. 99.     * @throws IOException   
  100. 100.     */    
  101. 101.    public static void main(String[] args) throws IOException {    
  102. 102.        NIOServer server = new NIOServer();    
  103. 103.        server.initServer(8000);    
  104. 104.        server.listen();    
  105. 105.    }    
  106. 106.    
  107. 107. }    
  
客戶端:
  1.  1. package cn.nio;    
  2. 2.    
  3. 3. import java.io.IOException;    
  4. 4. import java.net.InetSocketAddress;    
  5. 5. import java.nio.ByteBuffer;    
  6. 6. import java.nio.channels.SelectionKey;    
  7. 7. import java.nio.channels.Selector;    
  8. 8. import java.nio.channels.SocketChannel;    
  9. 9. import java.util.Iterator;    
  10. 10.    
  11. 11. /**  
  12. 12. * NIO客戶端  
  13. 13. *  
  14. 14. */    
  15. 15. public class NIOClient {    
  16. 16.    //通道管理器    
  17. 17.    private Selector selector;    
  18. 18.    
  19. 19.    /**  
  20. 20.     * 得到一個Socket通道,並對該通道作一些初始化的工做  
  21. 21.     * @param ip 鏈接的服務器的ip  
  22. 22.     * @param port  鏈接的服務器的端口號           
  23. 23.     * @throws IOException  
  24. 24.     */    
  25. 25.    public void initClient(String ip,int port) throws IOException {    
  26. 26.        // 得到一個Socket通道    
  27. 27.        SocketChannel channel = SocketChannel.open();    
  28. 28.        // 設置通道爲非阻塞    
  29. 29.        channel.configureBlocking(false);    
  30. 30.        // 得到一個通道管理器    
  31. 31.        this.selector = Selector.open();    
  32. 32.            
  33. 33.        // 客戶端鏈接服務器,其實方法執行並無實現鏈接,須要在listen()方法中調    
  34. 34.        //用channel.finishConnect();才能完成鏈接    
  35. 35.        channel.connect(new InetSocketAddress(ip,port));    
  36. 36.        //將通道管理器和該通道綁定,併爲該通道註冊SelectionKey.OP_CONNECT事件。    
  37. 37.        channel.register(selector, SelectionKey.OP_CONNECT);    
  38. 38.    }    
  39. 39.    
  40. 40.    /**  
  41. 41.     * 採用輪詢的方式監聽selector上是否有須要處理的事件,若是有,則進行處理  
  42. 42.     * @throws IOException  
  43. 43.     */    
  44. 44.    @SuppressWarnings("unchecked")    
  45. 45.    public void listen() throws IOException {    
  46. 46.        // 輪詢訪問selector    
  47. 47.        while (true) {    
  48. 48.            selector.select();    
  49. 49.            // 得到selector中選中的項的迭代器    
  50. 50.            Iterator ite = this.selector.selectedKeys().iterator();    
  51. 51.            while (ite.hasNext()) {    
  52. 52.                SelectionKey key = (SelectionKey) ite.next();    
  53. 53.                // 刪除已選的key,以防重複處理    
  54. 54.                ite.remove();    
  55. 55.                // 鏈接事件發生    
  56. 56.                if (key.isConnectable()) {    
  57. 57.                    SocketChannel channel = (SocketChannel) key    
  58. 58.                            .channel();    
  59. 59.                    // 若是正在鏈接,則完成鏈接    
  60. 60.                    if(channel.isConnectionPending()){    
  61. 61.                        channel.finishConnect();    
  62. 62.                            
  63. 63.                    }    
  64. 64.                    // 設置成非阻塞    
  65. 65.                    channel.configureBlocking(false);    
  66. 66.    
  67. 67.                    //在這裏能夠給服務端發送信息哦    
  68. 68.                    channel.write(ByteBuffer.wrap(new String("向服務端發送了一條信息").getBytes()));    
  69. 69.                    //在和服務端鏈接成功以後,爲了能夠接收到服務端的信息,須要給通道設置讀的權限。    
  70. 70.                    channel.register(this.selector, SelectionKey.OP_READ);    
  71. 71.                        
  72. 72.                    // 得到了可讀的事件    
  73. 73.                } else if (key.isReadable()) {    
  74. 74.                        read(key);    
  75. 75.                }    
  76. 76.    
  77. 77.            }    
  78. 78.    
  79. 79.        }    
  80. 80.    }    
  81. 81.    /**  
  82. 82.     * 處理讀取服務端發來的信息 的事件  
  83. 83.     * @param key  
  84. 84.     * @throws IOException   
  85. 85.     */    
  86. 86.    public void read(SelectionKey key) throws IOException{    
  87. 87.        //和服務端的read方法同樣    
  88. 88.    }    
  89. 89.        
  90. 90.        
  91. 91.    /**  
  92. 92.     * 啓動客戶端測試  
  93. 93.     * @throws IOException   
  94. 94.     */    
  95. 95.    public static void main(String[] args) throws IOException {    
  96. 96.        NIOClient client = new NIOClient();    
  97. 97.        client.initClient("localhost",8000);    
  98. 98.        client.listen();    
  99. 99.    }    
  100. 100.    
  101. 101. }    
轉載自:https://www.cnblogs.com/zedosu/p/6666984.html
相關文章
相關標籤/搜索