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 因此能够放入死循环线程中,因此基本能够立马收到断开链接的消息 }