this should not happen all the time
it’s been a while, but as I remember when you cancel a channel you cancel it’s key also and key is added to cancelled key set. When next selection occurs then that set is cleared and key is deregistered. This all happens before selector returns selected key set, so cancelled key cannot be in new selected key set.
Problem might be if you cancel SelectionKey in the same time when selector returns selected key, so after selector returned selected key set with your key in it and before you do something with it. I think chance of this happening isn’t high at all. In this cases you can check if key is canceled with isValid(), which turns to false immediatly when key is cancelled.
Another thing, you don’t need to do readyOps() and mess with bit operators, you can use SelectionKey.isReadable(), isWritable(), isAcceptable(). Also I speculate that they do isValid() in them, so they return false instead of throwing a exception.
So my guess is that you are doing something wrong, like forgetting to remove key from selected key set when you are done with it…
note: I just wrote a server / client once… this information might not be true.