posts - 1,  comments - 25,  trackbacks - 0

          java nio小結

          根據(jù)網(wǎng)上的資料做些整理

          Java NIO API詳解

          http://www.aygfsteel.com/19851985lili/articles/93524.html

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

          BIO 方式使得整個處理過程和連接是綁定的,只要連接建立,無論客戶端是否有消息發(fā)送,都要進行等待處理,一定程度上浪費了服務器端的硬件資源,因此就有了NIO 方式。Java 對于 NIO 方式的支持是通過 Channel和 Selector 方式來實現(xiàn),采用的方法為向 Channel注冊感興趣的事件,然后通過 Selector 來獲取到發(fā)生了事件的 key,如發(fā)生了相應的事件,則進行相應的處理,否則則不做任何處理,是典型的Reactor 模式,按照這樣的方式,就不用像 BIO 方式一樣,即使在沒有消息的情況下也需要占據(jù)一個線程來阻塞讀取消息,從而提升服務器的使用效率, 為實現(xiàn) TCP/IP+NIO 方式的系統(tǒng)間通訊, Java 提供了 SocketChannel和 ServerSocketChannel兩個關鍵的類,網(wǎng)絡 IO 的操作則改為通過ByteBuffer 來實現(xiàn),具體的基于 java實現(xiàn)TCP/IP+NIO 方式的通訊的方法如下所示。

          服務器端:

          復制代碼
          package com.eric.test.nio;

          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 {
          /*標志數(shù)字*/
          private static int flag = 0;
          /*定義緩沖區(qū)大小*/
          private static int block = 4096;
          /*接收緩沖區(qū)*/
          private static ByteBuffer receiveBuffer = ByteBuffer.allocate(block);
          /*發(fā)送緩沖區(qū)*/
          private static ByteBuffer sendBuffer = ByteBuffer.allocate(block);
          /*定義Selector*/
          private Selector selector;

          public NIOServer(int port) throws IOException{
          //打開服務器套接字通道
          ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

          //服務器配置為非阻塞
          serverSocketChannel.configureBlocking(false);

          //檢索與此服務器套接字通道關聯(lián)的套接字
          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:");
          }
          //監(jiān)聽
          public void listen() throws IOException{

          while(true){
          //監(jiān)控所有注冊的 channel ,當其中有注冊的 IO 操作可以進行時,該函數(shù)返回,并將對應的 SelectionKey 加入 selected-key set
          selector.select();

          //Selected-key set 代表了所有通過 select() 方法監(jiān)測到可以進行 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()){

          //返回創(chuàng)建此鍵的通道
          serverSocketChannel = (ServerSocketChannel)selectionKey.channel();

          //接受客戶端建立連接的請求,并返回 SocketChannel 對象
          socketChannel = serverSocketChannel.accept();

          //配置為非阻塞
          socketChannel.configureBlocking(false);

          //注冊到selector
          socketChannel.register(selector, SelectionKey.OP_READ);

          }else if(selectionKey.isReadable()){
          //返回為之創(chuàng)建此鍵的通道
          socketChannel = (SocketChannel)selectionKey.channel();

          //將緩沖區(qū)清空,以備下次讀取
          receiveBuffer.clear();

          //將發(fā)送來的數(shù)據(jù)讀取到緩沖區(qū)

          count = socketChannel.read(receiveBuffer);


          if(count>0){
          receiveText = new String(receiveBuffer.array(),0,count);
          System.out.println("服務器端接受到的數(shù)據(jù)---"+receiveText);
          socketChannel.register(selector, SelectionKey.OP_WRITE);
          }
          }else if (selectionKey.isWritable()) {
          //將緩沖區(qū)清空以備下次寫入
          sendBuffer.clear();

          // 返回為之創(chuàng)建此鍵的通道。
          socketChannel = (SocketChannel) selectionKey.channel();

          sendText="message from server--" + flag++;
          //向緩沖區(qū)中輸入數(shù)據(jù)
          sendBuffer.put(sendText.getBytes());

          //將緩沖區(qū)各標志復位,因為向里面put了數(shù)據(jù)標志被改變要想從中讀取數(shù)據(jù)發(fā)向服務器,就要復位
          sendBuffer.flip();

          //輸出到通道
          socketChannel.write(sendBuffer);

          System.out.println("服務器端向客戶端發(fā)送數(shù)據(jù)--:"+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.eric.test.nio;

          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 {
          /*標識數(shù)字*/
          private static int flag = 0;
          /*緩沖區(qū)大小*/
          private static int BLOCK = 4096;
          /*接受數(shù)據(jù)緩沖區(qū)*/
          private static ByteBuffer sendBuffer = ByteBuffer.allocate(BLOCK);
          /*發(fā)送數(shù)據(jù)緩沖區(qū)*/
          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 操作準備就緒。
          //監(jiān)控所有注冊的 channel ,當其中有注冊的 IO 操作可以進行時,該函數(shù)返回,并將對應的 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();
          //將緩沖區(qū)清空以備下次讀取
          receiveBuffer.clear();

          //讀取服務器發(fā)送來的數(shù)據(jù)到緩沖區(qū)中
          count=socketChannel.read(receiveBuffer);

          if(count>0){
          receiveText = new String( receiveBuffer.array(),0,count);
          System.out.println("客戶端接受服務器端數(shù)據(jù)--:"+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());
          //將緩沖區(qū)各標志復位,因為向里面put了數(shù)據(jù)標志被改變要想從中讀取數(shù)據(jù)發(fā)向服務器,就要復位
          sendBuffer.flip();

          socketChannel.write(sendBuffer);
          System.out.println("客戶端向服務器端發(fā)送數(shù)據(jù)--:"+sendText);
          socketChannel.register(selector, SelectionKey.OP_READ);
          }
          }
          selectionKeys.clear();
          }
          }
          }
          復制代碼

          小結:之前對Selector注冊事件和SocketChannel有點小困惑。SocketChannel就像一根水管,當監(jiān)聽到寫事件時,就往管道寫數(shù)據(jù);當監(jiān)聽到讀事件時,就從管道讀出數(shù)據(jù)。

          posted on 2012-04-25 15:34 Daniel 閱讀(450) 評論(1)  編輯  收藏 所屬分類: CoreJava

          FeedBack:
          # re: java nio小結[未登錄]
          2012-08-27 17:45 | 小李子
          非常感謝,看了好多別人的文章 都好難理解,
          把你的從頭到尾過了一遍,基本的原理已經(jīng)清楚了.謝謝  回復  更多評論
            
          <2025年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          常用鏈接

          留言簿(3)

          隨筆檔案

          文章分類

          文章檔案

          相冊

          搜索

          •  

          最新評論

          主站蜘蛛池模板: 宁津县| 沧州市| 莱西市| 霍林郭勒市| 承德市| 东方市| 永丰县| 彝良县| 平邑县| 宁阳县| 商丘市| 吴川市| 大竹县| 黑水县| 大兴区| 稷山县| 英超| 永清县| 台安县| 右玉县| 乌鲁木齐市| 望谟县| 盐边县| 六安市| 平罗县| 张家界市| 张家港市| 白沙| 潮安县| 普兰店市| 荔波县| 安新县| 宁远县| 沁水县| 安福县| 开封市| 方正县| 婺源县| 吉安县| 寿光市| 庆云县|