NIO原理

李德伦 发布于 2015/06/05 16:02
阅读 584
收藏 6

NIO服务器端:


1. 打开ServerSocketChannel,用于监听客户端的连接,它是所有客户端连接的父管道,代码示例如下:
      ServerSocketChannel acceptorSvr = ServerSocketChannel.open() ;

2. 绑定监听接口,设置连接为非阻塞模式,示例代码如下: 
      acceptorSvr.socket.bind(new InetSocketAddress(InetAddress.getByName("IP"),port)) ;
      acceptorSvr.configureBlocking(false) ;

3. 创建Reactor线程, 创建多路复用器并启动线程, 代码如下: 
      Selector selector  = Selector.open() ;
      new Thread(new ReactorTask()).start(); 

4. 将ServerSocketChannel注册到Reactor线程的多路复用器Selector上, 监听ACCEPT事件,代码如下:
      SelectionKey  key  = acceptroSvr.register(selector, selectionKey.OP_ACCEPT, ioHandler) ;

5. 多用复用器在线程run方法的无线循环体内轮询准备就绪的Key, 代码如下:
     int num = selector.select() ;
    Set selectedKeys = selector.selectedKeys() ;
    Iterator it = selectedKeys.iterator() ; 
    while(true){
              SelectionKey key = (SelectionKey) it.next() ;
              // ... deal with I/O event...
     }

6. 多路复用器监听到有新的客户端接入, 处理新的接入请求, 完成TCP三次握手,建立物理链路,代码示例如下:
      SocketChannel channel  = svrChannel.accept() ;

7.  设置客户端链路为非阻塞模式,示例代码如下:
      channel.configureBlocking(false) ;
      channel.socket().setReuseAddress(true);
      .... 

8.  将新接入的客户端连接注册到Reactor线程的多路复用器上, 监听读操作,用来读取客户端发送的网络消息,代码如下:
       SelectionKey  key = socketChannel.register(selector, SelectionKey.OP_READ, ioHandler) ;

9.  异步读取客户端请求消息到缓冲区,代码示例如下:
       int readNumber = channel.read(receivedBuffer); 

10.  对ByteBuffer进行编解码, 如果有半包消息指针reset,继续读取后续的报文, 将解码成的消息封装成Task,投递到业务线程池中,进行业务逻辑编排,代码示例如下:
       Object   message = null ;
       while(buffer.hasRemain()) {

               byteBuffer.mark() ;
               Object message = decode(byteBuffer) ;
               if(message ==  null) {
                      byteBuffer.reset() ;
                      break ;
                }
                messageList.add(message) ;
        }
        if(!byteBuffer.hasRemain())
                 byteBuffer.clear() ;
        else 
                 byteBuffer.compact() ;

         if(messageList != null & !messageList.isEmpty()) {

                  for(Object messageE : messageList) {
                         handlerTask(messageE) ;
                  }
          }

11.   将POJO对象encode成ByteBuffer,调用SocketChannel的异步write接口, 将消息异步发送给客户端, 代码示例如下:
         socketChannel.write(buffer) ;




----------------------------------------------------------------------------------------------------------------NIO客户端:



1.  打开SocketChannel,绑定客户端本地地址(可选,默认系统会随机分配一个可用的本地地址),示例代码如下:
         SocketChannel clientChannel = SocketChannel.open() ;

2.  设置SocketChannel为非阻塞模式,同时设置客户端连接的TCP参数,示例代码如下:
         clientChannel.configureBlocking(false) ;
         socket.setReuseAddress(true) ;
         socket.setReceiveBufferSize(BUFFER_SIZE) ;
         socket.setSendBufferSize(BUFFER_SIZE) ;

3.  异步连接服务端,示例代码如下:
         boolean connected = clientChannel.connect(new InetSocketAddress("ip",port)) ;

4.  判断是否连接成,如果连接成功,则直接注册读状态位到多路复用器中,如果当前没有连接成功(异步连接,返回false,说明客户端已经发送sync包,服务端没有返回ack包,物理链路还没有建立),示例代码如下:
        if(connected) {
              clientChannel.register(selector, SelectionKey.OP_READ, ioHandler) ;
        }else {
              clientChannel.register(selector, SelectionKey.OP_CONNECT, ioHandler) ;
        }

5.  向Reactor线程的多路复用器注册OP_CONNECT状态位,监听服务端的TCP ACK应答 , 示例代码如上

6.  创建Reactor线程,创建多路复用器并启动线程, 代码如下: 
           Selector selector  =  Selector.open() ;
           new Thread(new ReactorTask()).start() ;

7.  多路复用器在线程run方法的无限循环体内轮询准备就绪的Key,代码如下:
           int num = selector.select() ;
           Set selectedKey = selector.selectedKeys() ;
           Iterator it = selectedKeys.iterator() ;
           while(true) {
                SelectionKey key = (SelectionKey) it.next() ;
                 //   deal  with  I/O event .. 
           }

8.  接收connect事件进行处理, 示例代码如下:
          if(key.isConnectable()) {
                //handlerConnect() ;
          }

9.  判断连接结果,如果连接成功,注册读事件到多路复用器,代码示例如下:
           if(channel.finishConnect())
                   registerRead() ;

10.  注册读事件到多路复用器,示例代码如下:
          clientChannel.register(selector, SelectionKey.OP_READ, ioHandler) ;

11.  异步读取客户端请求消息到缓冲区,代码示例如下:
       int readNumber = channel.read(receivedBuffer); 

12.  对ByteBuffer进行编解码, 如果有半包消息指针reset,继续读取后续的报文, 将解码成的消息封装成Task,投递到业务线程池中,进行业务逻辑编排,代码示例如下:
       Object   message = null ;
       while(buffer.hasRemain()) {

               byteBuffer.mark() ;
               Object message = decode(byteBuffer) ;
               if(message ==  null) {
                      byteBuffer.reset() ;
                      break ;
                }
                messageList.add(message) ;
        }
        if(!byteBuffer.hasRemain())
                 byteBuffer.clear() ;
        else 
                 byteBuffer.compact() ;

         if(messageList != null & !messageList.isEmpty()) {

                  for(Object messageE : messageList) {
                         handlerTask(messageE) ;
                  }
          }

13.   将POJO对象encode成ByteBuffer,调用SocketChannel的异步write接口, 将消息异步发送给客户端, 代码示例如下:
         socketChannel.write(buffer) ;

加载中
OSCHINA
登录后可查看更多优质内容
返回顶部
顶部