107

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

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

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

4

9 に答える 9

208

接続の現在の状態を知らせる TCP API はありません。ソケットの現在の状態を教えてisConnected()ください。同じことではありません。isClosed()

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

  2. isClosed()このソケットを閉じたかどうかを示します。取得するまで、false を返します。

  3. ピアが正常な方法で接続を閉じた場合

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

    • 書き込みはIOException: 'connection reset by peer' をスローし、最終的にはバッファリングの遅延が発生します。

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

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

  6. 他の場所で読むかもしれないこととは反対に、これはわかりClosedChannelExceptionません。[どちらでもないSocketException: socket closed.]チャンネル閉じてから使い続けたことだけを教えてくれます。つまり、ユーザー側のプログラミング エラーです。閉じられた接続を示すものではありません。

  7. Windows XP で Java 7 を使用したいくつかの実験の結果、次のようにも見えます。

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

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

于 2012-04-20T06:00:43.773 に答える
11

さまざまなメッセージング プロトコルでは、互いにハートビートを維持する (ping パケットを送信し続ける) のが一般的な方法であり、パケットは非常に大きくする必要はありません。プローブ メカニズムにより、TCP が一般にそれを認識する前であっても、切断されたクライアントを検出できます (TCP タイムアウトははるかに長い) プローブを送信し、応答を 5 秒間待ちます。後続のプローブでは、プレーヤーが切断されます。

また、関連する質問

于 2012-04-20T05:37:02.100 に答える
2

投稿された他の回答が表示されますが、ゲームをプレイしているクライアントと対話していると思うので、別のアプローチを提案するかもしれません(場合によっては BufferedReader が確実に有効です)。

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

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

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

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

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

于 2012-04-20T05:40:27.057 に答える
1

これはtcp接続の性質であると思います。その標準では、アウト接続がなくなったと結論付けるまでに、送信で約6分の無音が必要です。したがって、この問題の正確な解決策を見つけることができないと思います。おそらくより良い方法は、サーバーがユーザー接続が閉じられていると想定するタイミングを推測するための便利なコードを書くことです。

于 2012-04-20T05:55:20.703 に答える
1

@ 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 !");
于 2019-01-03T12:29:26.170 に答える