4

最小限のJMSプロバイダーがあり、UDPを介してトピックメッセージを送信し、TCPを介してメッセージをキューに入れます。単一のセレクターを使用して、UDPおよびTCP選択キーを処理します(SocketChannelsとDatagramChannelsの両方を登録します)。

私の問題は、UDPパケットを送受信するだけですべてがうまくいくが、TCPソケットへの書き込みを開始するとすぐに(Selector.wakeup()を使用してセレクターに実際の書き込みを行わせる)、セレクターが無限ループに入るということです。ループし、空の選択キーセットを返し、100%のCPUを消費します。

メインループのコード(やや簡略化)は次のとおりです。

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();
   }
  }
 }

また、UDP送信の場合(TCP送信も同様です):

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

それで、ここで何が問題になっていますか?UDPパケットとTCPパケットを処理するために2つの異なるセレクターを使用する必要がありますか?

4

4 に答える 4

1

select()メソッドの戻り値を確認することをお勧めします。

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

ループがどこにあるかを確認するためにデバッグを試みましたか?

編集:

  • イテレータで「remove()」を呼び出す代わりに、イテレータを繰り返し処理した後にselectedKeys.clear()を呼び出すことをお勧めします。イテレータの実装が、基になるセットからイテレータを削除しない可能性があります。
  • 接続されているチャネルにOP_CONNECTを登録していないことを確認してください。
于 2010-11-02T12:01:28.390 に答える
1

Java 1.6.0_22にアップグレードすると、問題は解決しました。

于 2010-11-02T16:37:41.947 に答える
1

おそらくIOExceptionが発生し、空のcatchブロックでそれを無視しています。絶対にしないでください。そして、IOExceptionの後で続行することは、実際には決して正しいアクションではありません。私がオフハンドと考えることができるそのルールの唯一の例外はSocketTimeoutExceptionであり、あなたは非ブロッキングモードになっているので、それらを取得することはなく、とにかくセレクターでそれらを取得することはありません。接続、受け入れ、読み取り、書き込みを処理するメソッドの内容を確認したいと思います。

于 2010-11-02T23:38:25.457 に答える
0

着信ネットワーク接続ごとに1つのスレッドを持つように設計を変更します。

1つのスレッドを使用して複数のTCPソケットで着信メッセージを処理する場合は、セレクターを使用する必要があります。各ソケットをセレクターに登録してからselect()、そのうちの1つでデータが利用可能になるまでブロックします。次に、各キーをループして、待機中のデータを処理します。これは私がCコードを書くときにいつも使っていた方法であり、うまくいくでしょうが、Javaでそれを行うための最良の方法ではないと思います。

Javaには優れたネイティブスレッドサポートがありますが、Cにはありません。TCPソケットごとに1つのスレッドを持ち、セレクターをまったく使用しない方がはるかに理にかなっていると思います。ソケットに対して読み取り操作を実行するだけの場合、データが到着するか、ソケットが閉じられるまで、スレッドはブロックされます。これは、登録されているチャネルを1つだけ選択するのと実質的に同じです。

これを1つのスレッドでのみ機能させたい場合は、着信接続が必要なTCPソケットに対してのみセレクターを使用する必要があります。このように、への呼び出しselect()が戻るのは、ソケットの1つで待機している着信データがある場合のみです。そのスレッドは他のすべての時間にスリープ状態になり、他の操作でスリープを解除することはありません。

于 2010-11-02T11:59:15.503 に答える