代码之家  ›  专栏  ›  技术社区  ›  G B

Selector.select()开始无限循环

  •  4
  • G B  · 技术社区  · 15 年前

    我有一个最小的JMS提供程序,它通过UDP发送主题消息,并通过TCP排队消息。 我使用一个选择器来处理UDP和TCP选择键(同时注册SocketChannels和DatagramChannels)。

    主回路的代码(略为简化)为:

    public void run() {
      while (!isInterrupted()) {
       try {
        selector.select();
       } catch (final IOException ex) {
        break;
       }
    
      final Iterator<SelectionKey> selKeys = selector.selectedKeys().iterator();
      while (selKeys.hasNext()) {
        final SelectionKey key = selKeys.next();
        selKeys.remove();
        if (key.isValid()) {
         if (key.isReadable()) {
          this.read(key);
         }
         if (key.isConnectable()) {
          this.connect(key);
         }
         if (key.isAcceptable()) {
          this.accept(key);
         }
         if (key.isWritable()) {
          this.write(key);
          key.cancel();
         }
        }
       }
       synchronized(waitingToWrite) {
        for (final SelectableChannel channel: waitingToWrite) {
         try {
          channel.register(selector, SelectionKey.OP_WRITE);
         } catch (ClosedChannelException ex) {
          // TODO: reopen
         }
        }
        waitingToWrite.clear();
       }
      }
     }
    

    public void udpSend(final String xmlString) throws IOException {
      synchronized(outbox) {
        outbox.add(xmlString);
      }
      synchronized(waitingToWrite) {
        waitingToWrite.add(dataOutChannel);
      }
      selector.wakeup();
    }
    

    那么,这里怎么了?我应该使用两个不同的选择器来处理UDP和TCP数据包吗?

    4 回复  |  直到 15 年前
        1
  •  1
  •   krico    15 年前

    我建议您检查select()方法的返回值。

    try {
     if(selector.select() == 0) continue;
    } catch (final IOException ex) {
     break;
    }
    

    你试过调试看看循环在哪里吗?

    编辑:

    • 检查您没有在已连接的频道上注册OP_CONNECT。
        2
  •  1
  •   G B    15 年前

        3
  •  1
  •   user207421    15 年前

    您可能得到一个IOException,并在空catch块中忽略它。 做那件事。在IOException之后继续实际上永远都不是正确的操作。我能马上想到的唯一例外是SocketTimeoutException,而且您处于非阻塞模式,所以您不会得到这些,而且无论如何也不会在选择器上得到它们。我想看看处理connect、accept、read和write的方法的内容。

        4
  •  0
  •   Erick Robertson    15 年前

    当使用一个线程处理多个TCP套接字上的传入消息时,应使用选择器。向选择器注册每个套接字,然后 select() ,它将阻塞,直到其中一个上有可用的数据为止。然后循环遍历每个键并处理等待的数据。这是我在编写C代码时一直使用的方法,它会起作用,但我认为这不是用Java实现的最佳方法。

    Java有很好的本机线程支持,而C没有。我认为每个TCP套接字有一个线程而不使用选择器更有意义。如果只是对套接字执行读取操作,则线程将阻塞,直到数据到达或套接字关闭。这实际上与只使用一个注册通道进行选择是一样的。

    如果您想让它只与一个线程一起工作,那么应该只对需要传入连接的TCP套接字使用选择器。这样,唯一一次 当有传入数据在其中一个套接字上等待时将返回。该线程将在所有其他时间处于休眠状态,没有其他操作将其唤醒。