SocketChannel 异步的socket 连接

socketChannel 是java中用于建立异步socket连接的工具类,他和socket 非常类似。相似的 ServerSocketChannel 对应于 ServerSocket,即socket 的服务端。在java 中这两个类是实现异步socket的关键类。

    此外还有几个十分关键的工具类Selector ,顾名思义seletor 是异步socket中的一个通道选择器,我的理解中类似一个socket的map集合,凭借selector 的强大功能我们不再需要为每一个socket通道维持一个专门的线程,我们可以在一个线程里处理所有的连接。

先看看服务端的初始化

// 打开通道管理器
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判断

在线程中执行以下代码:(服务端)

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");
      }

 

客户端代码类似服务端,这里提供一个客户端判断连接断开的方法,,网上找的不知道可靠不

就是当连接状态可读的时候去试读取信息

 

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 所以可以放入死循环线程中,所以基本可以立马收到断开连接的消息   
}

 

转载于:https://my.oschina.net/u/2502529/blog/871650

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值