SelectionKey理解


SelectKey注冊了寫事件,不在合適的時間去除掉,會一直觸發寫事件,因為寫事件是代碼觸發的

client.register(selector, SelectionKey.OP_WRITE);

或者sk.interestOps(SelectionKey.OP_WRITE)

 執行了這以上任一代碼都會無限觸發寫事件,跟讀事件不同,一定注意

 

 

nio的select()的時候,只要數據通道允許寫,每次select()返回的OP_WRITE都是true。所以在nio的寫數據里面,我們在每次需要寫數據之前把數據放到緩沖區,並且注冊OP_WRITE,對selector進行wakeup(),這樣這一輪select()發現有OP_WRITE之后,將緩沖區數據寫入channel,清空緩沖區,並且反注冊OP_WRITE,寫數據完成。

這里面需要注意的是,每個SocketChannel只對應一個SelectionKey,也就是說,在上述的注冊和反注冊OP_WRITE的時候,不是通過channel.register()和key.cancel()做到的,而是通過key.interestOps()做到的。代碼如下:

public void write(MessageSession session, ByteBuffer buffer) throws ClosedChannelException {
   SelectionKey key = session.key();
   if((key.interestOps() & SelectionKey.OP_WRITE) == 0) {
    key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
   }
   try {
   writebuf.put(buffer);
   } catch(Exception e) {
    System.out.println("want put:"+buffer.remaining()+", left:"+writebuf.remaining());
    e.printStackTrace();
   }
   selector.wakeup();
}

.....

while(true) {
   selector.select();
   .....
        if(key.isWritable()) {
         MessageSession session = (MessageSession)key.attachment();
         //System.out.println("Select a write");
         synchronized(session) {
          writebuf.flip();
          SocketChannel channel = (SocketChannel)key.channel();
          int count = channel.write(writebuf);
          //System.out.println("write "+count+" bytes");
          writebuf.clear();
          key.interestOps(SelectionKey.OP_READ);
         }
        }
        ......
    }

要點一:不推薦直接寫channel,而是通過緩存和attachment傳入要寫的數據,改變interestOps()來寫數據;

要點二:每個channel只對應一個SelectionKey,所以,只能改變interestOps(),不能register()和cancel()。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM