1. @Slf4j
  2. public class ChannelDemo6 {
  3. public static void main(String[] args) {
  4. try (ServerSocketChannel channel = ServerSocketChannel.open()) {
  5. channel.bind(new InetSocketAddress(8080));
  6. System.out.println(channel);
  7. Selector selector = Selector.open();
  8. channel.configureBlocking(false);
  9. channel.register(selector, SelectionKey.OP_ACCEPT);
  10. while (true) {
  11. int count = selector.select();
  12. // int count = selector.selectNow();
  13. log.debug("select count: {}", count);
  14. // if(count <= 0) {
  15. // continue;
  16. // }
  17. // 获取所有事件
  18. Set<SelectionKey> keys = selector.selectedKeys();
  19. // 遍历所有事件,逐一处理
  20. Iterator<SelectionKey> iter = keys.iterator();
  21. while (iter.hasNext()) {
  22. SelectionKey key = iter.next();
  23. // 判断事件类型
  24. if (key.isAcceptable()) {
  25. ServerSocketChannel c = (ServerSocketChannel) key.channel();
  26. // 必须处理
  27. SocketChannel sc = c.accept();
  28. log.debug("{}", sc);
  29. }
  30. // 处理完毕,必须将事件移除
  31. iter.remove();
  32. }
  33. }
  34. } catch (IOException e) {
  35. e.printStackTrace();
  36. }
  37. }
  38. }


💡 为何要 iter.remove()

因为 select 在事件发生后,就会将相关的 key 放入 selectedKeys 集合,但不会在处理完后从 selectedKeys 集合中移除,需要我们自己编码删除。例如

  • 第一次触发了 ssckey 上的 accept 事件,没有移除 ssckey
  • 第二次触发了 sckey 上的 read 事件,但这时 selectedKeys 中还有上次的 ssckey ,在处理时因为没有真正的 serverSocket 连上了,就会导致空指针异常

💡 cancel 的作用

cancel 会取消注册在 selector 上的 channel,并从 keys 集合中删除 key 后续不会再监听事件