2012-12-23 11 views
12

私はちょっと上品な方法で扱うことができないと思っています。いくつかの質問に答えた別の解決策(thisthisなど)を試した後も、ケーブルを抜くことでソケットの切断を検出できませんでした。 。ソケットの切断を検出していますか?

NIOノンブロッキングソケットを使用していますが、サーバーの切断を検出する方法が見つからないことを除いて、すべてが完全に機能します。

私は、次のコードを持っている:SocketChannelsが読み込まれている間、私はケーブルを抜いて、スピンと0を返すSelector.select()開始、今私はまたは読み書きする機会を持っていない

while (true) { 
    handlePendingChanges(); 

    int selectedNum = selector.select(3000); 
    if (selectedNum > 0) { 
     SelectionKey key = null; 
     try { 
      Iterator<SelectionKey> keyIterator = selector.selelctedKeys().iterator(); 
      while (keyIterator.hasNext()) { 
       key = keyIterator.next(); 
       if (!key.isValid()) 
        continue; 

       System.out.println("key state: " + key.isReadable() + ", " + key.isWritable()); 

       if (key.isConnectable()) { 
        finishConnection(key); 
       } else if (key.isReadable()) { 
        onRead(key); 
       } else if (key.isWritable()) { 
        onWrite(key); 
       } 
      } 
     } catch (Exception e) { 
      e.printStackTrace(); 
      System.err.println("I am happy that I can catch some errors."); 
     } finally { 
      selector.selectedKeys().clear(); 
     } 
    } 
} 

チャンネルは、&の主な読書コードがif (selectedNum > 0)によって守られているので、今これはthis answerから、は、チャンネルが壊れて、選択()が返され、選択が返されると言われている最初の混乱です。のキーピア場合

select()はまだEJP's answerから同様の質問には、また0

を返し、キーが選択されていない、チャネルは、読み出し/書き込み可能なを示しますが、それはここでは明らかにそうではありません閉じたソケット:

  • 読み取りは、()THR)()-1
  • のreadLineを(nullを返します
  • readXXXを返します。 OWS EOFExceptionでは、他のどのX.

ないのいずれか、ここの場合のために、私は戻りませんそれらのキーからの読み取り、if (selectedNum > 0)コメントアウトし、それらが選択されているかどうかにかかわらず、すべてのキーを取得するためにselector.keys().iterator()を使用してみました-1(代わりに0が返されます)、これらのキーへの書き込みにはEOFExceptionがスローされません。私はただ1つのことを指摘しました。キーが選択されていないとしても、key.isWritable()が偽を返す間にkey.isReadable()が真を返します(これは私がOP_WRITEのキーを登録しなかったためかもしれません)。

私の質問は、Javaソケットがこれと同じように動作しているのか、間違っていたのかということです。

+1

OSがまだ接続が壊れていると宣言していない可能性があります。ケーブルを接続し直すと、理論上接続が再開される可能性があります。 – MvG

+0

はい、ケーブルを差し込んだときに接続が再開されることがあります。時には 'select()'を0に戻したままで、手動でキーをキャンセルすることはできません。 – neevek

答えて

19

TCP接続でタイマーとハートビートが必要であることが判明しました。

ネットワークケーブルを取り外すと、TCP接続が切断されないことがあります。何も送信しなければ、TCP/IPスタックは何も送っていない、ケーブルがどこかに行ったこと、またはピアPCが突然炎に掛かったことを知らない。このTCP接続は、何年も後にサーバーを再起動するまで開いていると見なすことができます。

このように考えると、どのようにしてTCPコネクションが他のエンドがネットワークから落ちたのかを知ることができます - それはネットワークから外れているので、あなたにその事実を伝えることはできません。

一部のシステムでは、サーバーに接続されているケーブルを取り外した場合にこれを検出できますが、一部のシステムではこれが検出されません。たとえば、ケーブルのもう一方の端にあるケーブルを抜いてください。検出されないイーサネットスイッチです。 1は常に

一つは非常に安い、TCP接続のために(つまり例えばピアにハートビートメッセージを送ったり、一定時間のために全く活性に基づいてTCP接続を閉じる)スーパーバイザタイマーを必要とする理由だ

TCPソケットでTCPキープアライブを有効にすることです。TCPキープアライブのデフォルトのタイムアウトは2時間になることが多いことに注意してください。

+0

あなたの説明は私の混乱を完全にクリアします。しかし、私が知りたいことがまだあります。ケーブルを差し込むと、接続が再開されます。時にはそうではありません。それはなぜですか? – neevek

+1

@neevek送信側がタイムアウトする可能性があります。送信側は他の端末が消滅したことを検出します。なぜなら、他の端末の間では、TCPスタックが接続をタイムアウトする前にケーブルを差し込むかどうかによって決まります。 – nos

+0

「スーパーバイザタイマーは常に必要」ではありません。地球上で最も使用されているアプリケーションのプロトコルであるHTTPには、たとえばHTTPがありません。書き込み時の読み取りタイムアウトとIOExceptionsで十分です。 – EJP

8

これらの回答はどちらも適用されません。最初のものは、接続が壊れている場合に関係し、2番目のもの(鉱山)は、相手が接続を閉じる場合に関係します。

TCP接続では、データが送受信されている場合を除いて、TCPは故意にこの種のもので堅牢であるように設計されているため、接続を切断するケーブルを引っ張ることについては基本的に何もありません。ピア・クロージングのようなローカル・アプリケーションを見なければならないものは何もありません。

TCPで切断された接続を検出する唯一の方法は、アプリケーション間でデータを送信したり、適切な間隔を置いた後に接続が失われたと解釈することです。

TCPキープアライブをオンに設定すると、切断された接続を検出できるようになります。また、システムによっては、ソケットごとのタイムアウトを制御することさえできます。しかし、Javaではなく、システムのデフォルトに悩まされています。変更されていない限り、2時間になるはずです。

コードでは、keyIterator.next()を呼び出した後にkeyIterator.remove()を呼び出す必要があります。

+0

こんにちは、@EJP、私はあなたが救助に来ることを知っていた、ありがとう。 *壊れた接続の検出を有効にするためにTCPキープアライブをオンにすることもできます*、壊れた接続検出ではここで何が果たす役割は?設定とキープアライブ設定の違いは何ですか? 'keyIterator.remove()'に関しては、私はfinallyブロックで 'selector.selectedKeys().clear()'を使っていました。 – neevek

+0

@neveekそれを逃した。 TCPキープアライブは、今すぐ、そして応答を必要とするパケットを毎回送信し、到着しない場合(再試行とタイムアウトを考慮して)、接続は切断されたとみなされます。次のI/O。 – EJP

+0

私はカスタムプロトコルを実装していません、私はHTTPを使用していますので、パケットが毎回ワイヤを介して送信され、そのパケットがHTTPヘッダーまたは本体の一部として解釈されるでしょうか?私がクライアントとしてkeep-aliveパケットを受信した場合、どうすればそれを処理できますか? – neevek

関連する問題