web-dev-qa-db-ja.com

JavaソケットAPI:接続が閉じられたかどうかを確認する方法は?

JavaソケットAPIの問題に直面しています。現在ゲームに接続しているプレイヤーの数を表示しようとしています。プレーヤーがいつ接続したかを判断するのは簡単です。ただし、ソケットAPIを使用してプレーヤーがいつ切断したかを判断することは不必要に難しいようです。

リモートで切断されたソケットでisConnected()を呼び出すと、常にtrueが返されるようです。同様に、リモートで閉じられたソケットでisClosed()を呼び出すと、常にfalseが返されるようです。ソケットが閉じられたかどうかを実際に判断するには、データを出力ストリームに書き込み、例外をキャッチする必要があることを読みました。これは、この状況を処理するための本当に不潔な方法のようです。ソケットがいつ閉じられたかを知るために、ネットワークを介してガベージメッセージを常にスパムする必要があります。

他の解決策はありますか?

79
Dan Brouwer

接続の現在の状態を通知するTCP APIはありません。 isConnected()およびisClosed()は、現在の状態を示しますソケットの。同じことではありません。

  1. isConnected()yoが接続されているかどうかthis socketを示します。あなたが持っているので、trueを返します。

  2. isClosed()yoが閉じられたかどうかを示しますこのソケットあなたが持っているまでfalseを返します。

  3. peerconnectionを正常に閉じた場合

    • read()は-1を返します
    • readLine()nullを返します
    • readXXX()は、他のXXXに対してEOFExceptionをスローします。

    • 書き込みはIOException:「ピアによる接続リセット」をスローし、最終的にはバッファリングの遅延が発生します。

  4. 他の理由で接続が切断された場合、書き込みは最終的に上記のようにIOExceptionをスローし、読み取りも同じことを行います。

  5. ピアがまだ接続されているが接続を使用していない場合、読み取りタイムアウトを使用できます。

  6. 他の場所で読むかもしれないことに反して、ClosedChannelExceptionはこれを教えてくれません。 [SocketException: socket closed.もしません] yochannel、を閉じて、それを使い続けたということだけを伝えます。言い換えれば、あなたの側のプログラミングエラーです。閉じられていることを示すものではありませんconnection。

  7. Windows Java 7でのいくつかの実験の結果、XPは次の場合にも表示されます。

    • OP_READで選択しています
    • select()はゼロより大きい値を返します
    • 関連するSelectionKeyはすでに無効です(key.isValid() == false

    ピアが接続をリセットしたことを意味します。ただし、これはJREバージョンまたはプラットフォームに固有の場合があります。

157
user207421

さまざまなメッセージングプロトコルでは、パケットを非常に大きくする必要がないため、互いにハートビートを維持する(pingパケットを送信し続ける)のが一般的です。プローブメカニズムにより、TCPが一般的に理解する前に切断されたクライアントを検出することができます(TCPタイムアウトははるかに高い)たとえば2〜3回のプローブに対して返信すると、プレーヤーは切断されます。

また、 関連する質問

8
Kalpak Gadre

投稿された他の答えを見ましたが、あなたはあなたのゲームをプレイしているクライアントと対話的であると思うので、別のアプローチを提案するかもしれません(一方、BufferedReaderは間違いなく有効です).

必要な場合は、「登録」の責任をクライアントに委任できます。つまり各ユーザーから受信した最後のメッセージのタイムスタンプを持つ接続ユーザーのコレクションがあります...クライアントがタイムアウトした場合、クライアントの再登録を強制しますが、それは以下の見積もりとアイデアにつながります。

ソケットが閉じられたかどうかを実際に判断するために、データを出力ストリームに書き込み、例外をキャッチする必要があることを読みました。これは、この状況を処理するための本当に不潔な方法のようです。

Javaコードがソケットを閉じ/切断しなかった場合、リモートホストが接続を閉じたことを他にどのように通知しますか?最終的に、try/catchは、ACTUALソケットでイベントをリッスンするポーラーが実行するのとほぼ同じことを実行します。以下を考慮してください。

  • ローカルシステムは、通知せずにソケットを閉じることができます。これは、単にSocketの実装です(つまり、ハードウェア/ドライバー/ファームウェア/状態の変化をポーリングしません)。
  • 新しいSocket(Proxy p)...複数の関係者(実際には6つのエンドポイント)があなたの接続を閉じている可能性があります...

抽象化された言語の特徴の1つは、あなたがマニューシャから抽象化されていることだと思います。 SqlConnectionのC#(try/finally)でキーワードを使用することを考えてみてください...ビジネスを行うためのコストです... try/catch/finallyは、Socketの使用に受け入れられ、必要なパターンだと思います。

2
Scottley

これはtcp接続の性質だと思います。標準では、送信接続がなくなったと判断するまでに、送信に約6分間の無音が必要です。したがって、この問題の正確な解決策を見つけることができるとは思わない。たぶん、より良い方法は、サーバーがユーザー接続が閉じられたと仮定すべきときを推測するための便利なコードを書くことです。

1

それは私がそれを処理する方法です

 while(true) {
        if((receiveMessage = receiveRead.readLine()) != null ) {  

        System.out.println("first message same :"+receiveMessage);
        System.out.println(receiveMessage);      

        }
        else if(receiveRead.readLine()==null)
        {

        System.out.println("Client has disconected: "+sock.isClosed()); 
        System.exit(1);
         }    } 

result.code == nullの場合

0
Petar Ceho

@ user207421が言うように、TCP/IPプロトコルアーキテクチャモデルのため、接続の現在の状態を知る方法はありません。そのため、サーバーは接続を閉じる前にユーザーに通知するか、自分で確認する必要があります。
これは、サーバーによってソケットが閉じられたことを知る方法を示す簡単な例です。

sockAdr = new InetSocketAddress(SERVER_HOSTNAME, SERVER_PORT);
socket = new Socket();
timeout = 5000;
socket.connect(sockAdr, timeout);
reader = new BufferedReader(new InputStreamReader(socket.getInputStream());
while ((data = reader.readLine())!=null) 
      log.e(TAG, "received -> " + data);
log.e(TAG, "Socket closed !");
0
ucMedia