java nio(reactor, selector, selectionKey)

a 夏天 提交于 2020-02-04 23:46:56

SocketChannel vs. ServerSocketChannel

  • 父类:SelectableChannel。Channel表现了一个可以进行IO操作的通道(比如,通过FileChannel,我们可以对文件进行读写操作)
  • ServerSocketChannel主要用在Server中,用于接收客户端的链接请求 
    SocketChannel则用于真正的读写数据,同时还可以用于客户端发送链接请求。
  • 真正实现读写数据操作的就是这些SocketChannel,上面的ServerSocketChannel只是负责接收连接请求。
  • 以下均简称为channel

 

 

channel vs. Selector

  • channel需要注册到selector上。channel可以注册到一个或多个Selector上以进行异步IO操作。

       channel.register(selector, SelectionKey.OP_ACCEPT);

       channel.register(selector, xxx, object); //attachment被存放在返回的SelectionKey中

       channel.keyFor(selector); //返回该channe在Selector上的注册关系所对应的SelectionKey。若无注册关系,返回null。

  • Selector可以同时监控多个SelectableChannel的IO状况,是异步IO的核心。

       Selector.open(); //静态方法,创建一个selector实例

       selector.select(); //selector通过调用select(),将注册的channel中有事件发生的取出来进行处理。监控所有注册的channel,当其中有注册的IO操作可以进行时,该函数返回,并将对应的SelectionKey加入selected-key set。

       selector.keys(); //所有注册在这个Selector上的channel

       selector.selectedKeys(); //所有通过select()方法监测到可以进行IO操作的channel

 

SelectionKey

  • 代表了Selector和SelectableChannel的注册关系

       key.attachment(); //返回SelectionKey的attachment,attachment可以在注册channel的时候指定。

       key.channel(); // 返回该SelectionKey对应的channel。

       key.selector(); // 返回该SelectionKey对应的Selector。

       key.interestOps(); //返回代表需要Selector监控的IO操作的bit mask

       key.readyOps(); //返回一个bit mask,代表在相应channel上可以进行的IO操作。

 

 

Demo1: Server端底层如何接受连接

   int n = selector.select();                                   // 得到selector所捕获的事件数量 
   if( n > 0 ){                                                        // 当真正捕获到事件时,才执行相应操作 
    Set selectedKeys = selector.selectedKeys();  // 获取捕获到的事件集合 
Iterator i = selectedKeys.iterator(); 
while(i.hasNext())               

SelectionKey s = (SelectionKey) i.next(); // 对事件一一处理

//一个key被处理完成后,就都被从就绪关键字(ready keys)列表中除去 
i.remove(); 
if(s.isAcceptable())                                   // 表示该事件为OP_ACCEPT事件 

// 从channel()中取得我们刚刚注册的ServerSocketChannel。 
// 为请求获取新的SocketChannel 
SocketChannel sc = ((ServerSocketChannel)s.channel()).accept().socket().getChannel(); 

sc.configureBlocking(false);                 // 设置SocketChannel为非阻塞方式 
sc.register(selector, SelectionKey.OP_READ |SelectionKey.OP_WRITE);  
                       // 将新的SocketChannel注册到selector中,注册事件为OP_READ和OP_WRITE 

else 

// 执行其他操作 

  } 
   }

 

Demo2: Server端底层如何读取channel上的数据

                SelectionKey s = (SelectionKey) i.next();                          // 对事件一一处理

//一个key被处理完成后,就都被从就绪关键字(ready keys)列表中除去 
i.remove(); 
                 ByteBuffer clientBuffer = ByteBuffer.allocate(4096); 
if (key.isReadable()) {                                                               // 读信息 
                     SocketChannel channel = (SocketChannel) key.channel();    // 获取相应的SocketChannel 
                     int count = channel.read(clientBuffer);                                   // 将数据读入clientBuffer 
                     if (count > 0) {                                                                       // 当有数据读入时 
                         clientBuffer.flip();                                                               // 反转此缓冲区 
                         CharBuffer charBuffer = decoder.decode(clientBuffer);     // 如果需要,对缓冲区中的字符进行解码 
                          ...                                                                                     // 处理数据 
                     } 
                 }

 

Demo3: Client端底层如何发起连接、写入数据

   InetSocketAddress addr = new InetSocketAddress(host,port); 
   //生成一个socketchannel 
   sc = SocketChannel.open();         
   //连接到server 
   sc.connect(addr);                       // 连接到server

   if(sc.finishConnect()){                // 当连接成功时,执行相应操作 
    ByteBuffer buffer = .....             // 准备数据 
    buffer.filp();                              // 反转此缓冲区 
    while(w_buff.hasRemaining())  // 发送数据到server 
          sc.write(w_buff);

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!