SocketChannel 異步的socket 鏈接

socketChannel 是java中用於創建異步socket鏈接的工具類,他和socket 很是相似。類似的 ServerSocketChannel 對應於 ServerSocket,即socket 的服務端。在java 中這兩個類是實現異步socket的關鍵類。java

    此外還有幾個十分關鍵的工具類Selector ,顧名思義seletor 是異步socket中的一個通道選擇器,個人理解中相似一個socket的map集合,憑藉selector 的強大功能咱們再也不須要爲每個socket通道維持一個專門的線程,咱們能夠在一個線程裏處理全部的鏈接。緩存

先看看服務端的初始化app

// 打開通道管理器
selector = Selector.open();
// 打開一個未綁定的服務端通道對象
serverChannel = ServerSocketChannel.open();
// 將此服務端對象綁定端口,Ip默認是本機的Ip,若是本機有多個ip,須要指定ip
serverChannel.socket().bind(new InetSocketAddress(PORT));
// 設定服務端爲非阻塞方式工做
serverChannel.configureBlocking(false);

 

// 把設定好的服務端註冊到通道管理器中    ,該操做會建立一個選擇鍵
serverChannel.register(selector, SelectionKey.OP_ACCEPT, BUFFER_SIZE);

必須通過註冊以後selector 通道管理器中的值纔會大於零異步

selector的selectionKey 有四種狀態,acceptable ,  connectable, readable,writable  可用isXXX判斷socket

在線程中執行如下代碼:(服務端)工具

while (selector.select() > 0) {             //此方法是一個阻塞操做
            //Log.e(TAG,"外層循環");
            Set<SelectionKey> selectionKeys= selector.selectedKeys();   // 獲取所有的已選擇鍵,
                                                                        //能夠直接移除單不可直接添加
             Iterator<SelectionKey>    it=   selectionKeys.iterator();
               while (it.hasNext()){
               SelectionKey skey=it.next();      //對已經註冊的socket的集合進行迭代
               it.remove();                       // 被移除的是集合selectionKeys中的對象不是selector的對象
                                                 //該操做用於結束內層循環   
               Boolean flag = false;
               if (skey.isAcceptable()) {                           //有新的鏈接請求
                  SocketChannel channel = serverChannel.accept();
                  String clientIp = getClientIp(skey,channel);         
                  channel.configureBlocking(false);                     //不阻塞
                  Log.e(TAG,"client online "+ clientIp);
                  new NSServerClientOnline(onlines, clientIp).run();    //自定義客戶端上線回調   
                  try {
                     channel.register(selector, SelectionKey.OP_READ); //從新註冊爲可讀狀態 
                  } catch (Exception e) {
                     flag = true;
                     skey.cancel();
                     if (skey.channel() != null) {
                        skey.channel().close();
                     }
                  }
//

               }
               if (skey.isReadable()) {                      
                  StringBuffer message = new StringBuffer();
                  SocketChannel channel = (SocketChannel) skey.channel();
                  String clientIp = getClientIp(skey,channel);
                  ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
                  try {
                     if(channel.read(buffer)<0){
                        flag = true;
                        skey.cancel();
                        if (skey.channel() != null) {
                           skey.channel().close();
                        }
                     }
                     while (channel.read(buffer) > 0) {
                     }
                     buffer.flip();                    //此方法刷新緩存的buffer 此方法慎用他回修改selectionKey的狀態
                     message.append(charse.decode(buffer));

                     if (null != message && message.length() > 0) {
                        Log.e(TAG,"服務端收到消息" + message);
                        new NSServerReceive(receives, message, clientIp).run();
                     }
                     skey.interestOps(SelectionKey.OP_READ);  //修改 讀寫狀態
                  } catch (Exception e) { 
                     flag = true;
                     skey.cancel();
                     if (skey.channel() != null) {
                        skey.channel().close();
                     }
                  }
               }
               if (flag) {
                        new NSServerClientOffline(offLines, getClientIp(skey, null)).run();
                    }
            }
         }
         instance().start();      // 若是異常退出大循環 重啓服務
      } catch (IOException e) {
         e.printStackTrace();
         Log.e(TAG," socekct server  crash");
      }

 

客戶端代碼相似服務端,這裏提供一個客戶端判斷鏈接斷開的方法,,網上找的不知道可靠不線程

就是當鏈接狀態可讀的時候去試讀取信息rest

 

if(clientChannel.read(buffer)<0){
   Log.e(TAG,"socket client read buffer");
   flag = true;
   skey.cancel();
   if (skey.channel() != null) {
      skey.channel().close();
   }
   break TT;             // 讀取的內容爲-1 判斷爲鏈接斷開
                         // 由於這是一個異步的socket 因此能夠放入死循環線程中,因此基本能夠立馬收到斷開鏈接的消息   
}
相關文章
相關標籤/搜索