2

次のような Discover プロセスを実装しています。

  • UDP ソケットを開いて、特定のポートでブロードキャスト応答をリッスンします
  • いくつかのリクエストを送信します(後で応答を期待します)
  • 一定時間後に UDP ソケットを閉じる

最初の呼び出しは機能します。しかし、他の呼び出しではバインド エラーが発生します。アドレスは既に使用されています: bind

Windows 7 を実行しています。いくつかのテストを行ったところ、channel.close(); の後にそれが見つかりました。Netstat はまだ提供します:

netstat -a -b -sp udp | grep 55224

UDP 0.0.0.0:55224 :

したがって、UDP ポートは OS レベルで開かれたままです。

Web を検索しましたが、OS レベルでのリークである可能性があります。いくつかの Java Datagram Socket の質問

NIO チャネルを使用するテストと使用しないテストの 2 つを実行しました (Web で見つかったテストから)。NIO バージョンでエラーを再現しましたが、NIO を使用しなくても機能します。

私は誰でも、NIO で動作させる方法を教えてくれます。対象となるプラットフォームは Android でt wan、常にブロードキャストをリッスンするわけではありませんが、繰り返しの期間だけリッスンします。

テストソケット

    public void testConnectCloseWithSocket() {
    long tCumulative = 0;
    int errAt = -1;
    System.out.println("start...");
    for (int i = 0; i < 4000; i++) {
        try {
            errAt = i;
            DatagramSocket result = new DatagramSocket(null);
            result.bind(new InetSocketAddress(InetAddress.getLocalHost(), 9005));
            result.close();

            //success at last
            tCumulative = 0;

        } catch (Exception e) {
            System.out.println("Error (at="+errAt+") (waited="+tCumulative+"ms): " + e.getMessage());

            tCumulative+=50;
            try {
                Thread.sleep(50);
            } catch (InterruptedException e1) {


            }
            i--;
        }
    }
    System.out.println("end...");

}

結果ソケット<

start... エラー (at=1319) (waited=0ms): アドレスは既に使用されています: バインドできません

エラー (at=1438) (waited=0ms): アドレスは既に使用されています: バインドできません

エラー (at=1587) (waited=0ms): アドレスは既に使用されています: バインドできません

エラー (at=1740) (waited=0ms): アドレスは既に使用されています: バインドできません

終わり...


いくつかのエラーが発生しましたが、ソケットは適切に閉じられます...これは私のニーズに合っています

チャンネルでテスト

    public void testConnectCloseWithChannel() {
    long tCumulative = 0;
    int errAt = -1;
    System.out.println("start...");
    for (int i = 0; i < 4000; i++) {
        try {
            errAt = i;
            Selector selector = Selector.open();
            DatagramChannel channel = DatagramChannel.open();
            channel.configureBlocking(true);
            channel.socket().bind(new InetSocketAddress(InetAddress.getLocalHost(), 9005));
            SelectionKey clientKey = channel.register(selector, SelectionKey.OP_READ);
            clientKey.cancel();
            channel.close();

            //success at last
            tCumulative = 0;

        } catch (Exception e) {
            System.out.println("Error (at="+errAt+") (waited="+tCumulative+"ms): " + e.getMessage());

            tCumulative+=50;
            try {
                Thread.sleep(tCumulative);
            } catch (InterruptedException e1) {


            }
            i--;
        }
    }
    System.out.println("end...");

}

注: channel.register がコメント化されている場合、テストは機能します ..


チャネルの結果

start... エラー (at=0) (waited=0ms): null エラー (at=0) (waited=50ms): アドレスは既に使用されています: bind

エラー (at=0) (waited=100ms): アドレスは既に使用されています: bind

エラー (at=0) (waited=150ms): アドレスは既に使用されています: bind ...


助けてくれてありがとう

4

2 に答える 2

3

いくつかのエラーが発生しましたが、ソケットは適切に閉じられます...これは私のニーズに合っています

いいえ、エラーが発生した場合は、チャネルが適切に閉じられていません。

closeブロックのfinally句で行う必要がありますtry

Selector selector = Selector.open();
try
{
  DatagramChannel channel = DatagramChannel.open();

  try
  {
    channel.configureBlocking(true);
    channel.socket().bind(
      new InetSocketAddress(InetAddress.getLocalHost(), 9005)
    );
    SelectionKey clientKey = channel.register(selector, SelectionKey.OP_READ);
    clientKey.cancel();
  }
  finally
  {
    channel.close();
  }
}
finally
{
  selector.close( )
}
于 2012-10-19T15:14:16.327 に答える
0

チャネルがセレクターに登録されている場合、チャネルのクローズの一部は次の select() まで延期されます。Selector、AbstractSelector、SelectorSpi、SelectableChannel、AbstractSelectableChannel の森のどこかに文書化されており、必要なときに見つけることができません。チャネルを閉じるときに選択ループとスレッド内にいる場合は、selectNow() を呼び出してすぐに行うことができます。

于 2012-10-19T21:17:21.460 に答える