java nio小結

來源:互聯網
上載者:User

Java NIO API詳解

http://www.blogjava.net/19851985lili/articles/93524.html

這篇文章對nio的api講解比較全,可以協助在宏觀上把握nio。

BIO 方式使得整個處理過程和串連是綁定的,只要串連建立,無論用戶端是否有訊息發送,都要進行等待處理,一定程度上浪費了伺服器端的硬體資源,因此就有了NIO 方式。Java 對於 NIO 方式的支援是通過 Channel和 Selector 方式來實現,採用的方法為向 Channel註冊感興趣的事件,然後通過 Selector 來擷取到發生了事件的 key,如發生了相應的事件,則進行相應的處理,否則則不做任何處理,是典型的Reactor 模式,按照這樣的方式,就不用像 BIO 方式一樣,即使在沒有訊息的情況下也需要佔據一個線程來阻塞讀取訊息,從而提升伺服器的使用效率, 為實現 TCP/IP+NIO 方式的系統間通訊, Java 提供了 SocketChannel和 ServerSocketChannel兩個關鍵的類,網路 IO 的操作則改為通過ByteBuffer 來實現,具體的基於 java實現TCP/IP+NIO 方式的通訊的方法如下所示。

伺服器端:

package com.flyoung;import java.io.IOException;import java.net.InetSocketAddress;import java.net.ServerSocket;import java.nio.ByteBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.ServerSocketChannel;import java.util.Iterator;import java.util.Set;import java.nio.channels.SocketChannel;public class NIOServer {    /*標誌數字*/    private static int flag = 0;    /*定義緩衝區大小*/    private static int block = 4096;    /*接收緩衝區*/    private static ByteBuffer receiveBuffer = ByteBuffer.allocate(block);    /*發送緩衝區*/    private static ByteBuffer sendBuffer = ByteBuffer.allocate(block);    /*定義Selector*/    private Selector selector;        public NIOServer(int port) throws IOException{        //開啟伺服器通訊端通道        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();        //伺服器配置為非阻塞        serverSocketChannel.configureBlocking(false);        //檢索與此伺服器通訊端通道關聯的通訊端        ServerSocket serverSocket = serverSocketChannel.socket();        //進行服務的綁定        serverSocket.bind(new InetSocketAddress(port));        //通過open()方法找到Selector        selector = Selector.open();        //註冊到selector        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);        System.out.println("Server Start -----8888:");    }    //監聽    public void listen() throws IOException{        while(true){            //監控所有註冊的 channel ,當其中有註冊的 IO 操作可以進行時,該函數返回,並將對應的 SelectionKey 加入 selected-key set            selector.select();            //Selected-key set 代表了所有通過 select() 方法監測到可以進行 IO 操作的 channel ,這個集合可以通過 selectedKeys() 拿到            Set<SelectionKey> selectionKeys = selector.selectedKeys();            Iterator<SelectionKey> iterator = selectionKeys.iterator();            while(iterator.hasNext()){                SelectionKey selectionKey = iterator.next();                handleKey(selectionKey);                iterator.remove();            }        }            }    //處理請求    public void handleKey(SelectionKey selectionKey) throws IOException{        //接受請求        ServerSocketChannel serverSocketChannel = null;        SocketChannel socketChannel = null;        String receiveText;        String sendText;        int count;        //測試此鍵的通道是否準備好接受新的通訊端串連        if(selectionKey.isAcceptable()){            //返回建立此鍵的通道            serverSocketChannel = (ServerSocketChannel)selectionKey.channel();            //接受用戶端建立串連的請求,並返回 SocketChannel 對象            socketChannel = serverSocketChannel.accept();            //配置為非阻塞            socketChannel.configureBlocking(false);            //註冊到selector            socketChannel.register(selector, SelectionKey.OP_READ);        }else if(selectionKey.isReadable()){            //返回為之建立此鍵的通道            socketChannel = (SocketChannel)selectionKey.channel();            //將緩衝區清空,以備下次讀取            receiveBuffer.clear();            //將發送來的資料讀取到緩衝區                        count = socketChannel.read(receiveBuffer);                                if(count>0){                receiveText = new String(receiveBuffer.array(),0,count);                System.out.println("伺服器端接受到的資料---"+receiveText);                socketChannel.register(selector, SelectionKey.OP_WRITE);            }        }else if (selectionKey.isWritable()) {              //將緩衝區清空以備下次寫入              sendBuffer.clear();              // 返回為之建立此鍵的通道。              socketChannel = (SocketChannel) selectionKey.channel();              sendText="message from server--" + flag++;              //向緩衝區中輸入資料              sendBuffer.put(sendText.getBytes());               //將緩衝區各標誌複位,因為向裡面put了資料標誌被改變要想從中讀取資料發向伺服器,就要複位              sendBuffer.flip();              //輸出到通道              socketChannel.write(sendBuffer);              System.out.println("伺服器端向用戶端發送資料--:"+sendText);              socketChannel.register(selector, SelectionKey.OP_READ);          }              }    public static void main(String[] args) throws IOException {        int port = 8888;         NIOServer server = new NIOServer(port);        server.listen();    }}

用戶端

package com.flyoung;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.Set;public class NIOClient {    /*標識數字*/      private static int flag = 0;      /*緩衝區大小*/      private static int BLOCK = 4096;      /*接受資料緩衝區*/      private static ByteBuffer sendBuffer = ByteBuffer.allocate(BLOCK);      /*發送資料緩衝區*/      private static ByteBuffer receiveBuffer = ByteBuffer.allocate(BLOCK);      /*伺服器端地址*/      private final static InetSocketAddress SERVER_ADDRESS = new InetSocketAddress(              "localhost", 8888);        public static void main(String[] args) throws IOException {          // 開啟socket通道          SocketChannel clientChannel = SocketChannel.open();          // 設定為非阻塞方式          clientChannel.configureBlocking(false);          // 開啟選取器          Selector selector = Selector.open();          // 註冊串連服務端socket動作          clientChannel.register(selector, SelectionKey.OP_CONNECT);          // 串連          clientChannel.connect(SERVER_ADDRESS);              SocketChannel socketChannel;        Set<SelectionKey> selectionKeys;            String receiveText;          String sendText;          int count=0;            while (true) {              //選擇一組鍵,其相應的通道已為 I/O 操作準備就緒。              //監控所有註冊的 channel ,當其中有註冊的 IO 操作可以進行時,該函數返回,並將對應的 SelectionKey 加入 selected-key set             selector.select();              //返回此選取器的已選擇鍵集。              selectionKeys = selector.selectedKeys();              //System.out.println(selectionKeys.size());              for(SelectionKey selectionKey:selectionKeys){                 //判斷是否為建立串連的事件                if (selectionKey.isConnectable()) {                      System.out.println("client connect");                      socketChannel = (SocketChannel) selectionKey.channel();  //                    // 判斷此通道上是否進行中串連操作。                      // 完成通訊端通道的串連過程。                      if (socketChannel.isConnectionPending()) {                         //完成串連的建立(TCP三向交握)                        socketChannel.finishConnect();                          System.out.println("完成串連!");                          sendBuffer.clear();                          sendBuffer.put("Hello,Server".getBytes());                          sendBuffer.flip();                          socketChannel.write(sendBuffer);                      }                      socketChannel.register(selector, SelectionKey.OP_READ);                  } else if (selectionKey.isReadable()) {                      socketChannel = (SocketChannel) selectionKey.channel();                      //將緩衝區清空以備下次讀取                      receiveBuffer.clear();                      //讀取伺服器發送來的資料到緩衝區中                      count=socketChannel.read(receiveBuffer);                      if(count>0){                          receiveText = new String( receiveBuffer.array(),0,count);                          System.out.println("用戶端接受伺服器端資料--:"+receiveText);                          socketChannel.register(selector, SelectionKey.OP_WRITE);                      }                    } else if (selectionKey.isWritable()) {                      sendBuffer.clear();                      socketChannel = (SocketChannel) selectionKey.channel();                      sendText = "message from client--" + (flag++);                      sendBuffer.put(sendText.getBytes());                       //將緩衝區各標誌複位,因為向裡面put了資料標誌被改變要想從中讀取資料發向伺服器,就要複位                      sendBuffer.flip();                      socketChannel.write(sendBuffer);                      System.out.println("用戶端向伺服器端發送資料--:"+sendText);                      socketChannel.register(selector, SelectionKey.OP_READ);                  }              }              selectionKeys.clear();          }      }  }

小結:之前對Selector註冊事件和SocketChannel有點小困惑。SocketChannel就像一根水管,當監聽到寫事件時,就往管道寫資料;當監聽到讀事件時,就從管道讀出資料。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.