2017-10-10 12 views
2

練習では、接続を待ち受け、ソケットを受け取り、ソケットをBlockingQueueにスローするスレッドを持つサーバーを実装する必要があります。プール内のワーカー・スレッドのセットは、キューを通過し、ソケットを介して入ってくるリクエストを処理します。Java:スレッドよりも多くの接続をキューを使って管理する

各クライアントはサーバーに接続し、多数の要求(次の要求を送信する前に応答を待っている)を送信し、完了すると結局切断します。

私の現在のアプローチは、各ワーカースレッドが待ち行列で待機し、ソケットを取得してから1つの要求を処理し、最後に(別のクライアントからの)別の要求を処理する前に。ワーカースレッドより多くのクライアントが存在するため、多くの接続がキューに入れられます。

このアプローチの問題:クライアントが何も送信しなくても、スレッドはクライアントによってブロックされます。可能な疑似ソリューション、すべてに満足のいくものではない:

  • inputStream上のコールavailable()、それは問題は0を返す場合、キューにバック接続を置く:これは、クライアントがまだ接続されているかどうかを検出することは不可能です。
  • 上記のとおり、クライアントがまだ接続されているかどうかを調べるには、socket.isClosed()またはsocket.isConnected()を使用します。問題:どちらの方法でもEJPによってうまく説明されているように、クライアントのハングアップは検出されません。Java socket API: How to tell if a connection has been closed?
  • クライアントが読み取りまたは書き込みを行っている場合にプローブが表示されます。問題:ブロックを読み込む(つまり、非アクティブなクライアントがキューをブロックする元の状態に戻す)書き込みは実際にクライアントに何かを送信し、テストを失敗させる。

この問題を解決する方法はありますか?私。切断されたクライアントと受動クライアントを区別することは可能ですか?

答えて

1

問題は、いくつかのトリックで解決できることが分かります。いくつかの人々との長い議論の後、私は仕事がreasonnable時間内に終わらために彼らのアイデアを組み合わせた:

  • ソケットを作成した後、それは、このようなブロッキング読み取りのみを一定時間ブロックすることを設定、100ミリ秒を言う:socket.setSoTimeout(100);
  • さらに、各接続の最後に成功した読み取りのタイムスタンプを記録します。 System.currentTimeMillis()
  • 原則(この原則の例外については以下を参照してください)を読む前に接続にavailable()を実行してください。この値が0を返す場合は、読み取る必要がないため、接続をキューに戻します。
  • この場合の例外は、available()が使用されていない場合です。タイムスタンプが古すぎる場合(たとえば、1秒以上)は、実際に接続をブロックする場合はread()を使用します。これは、ソケットのために上記で設定したSoTimeoutよりも時間がかかりません。 TimeoutExceptionが発生した場合は、接続をキューに戻します。 -1を読み取った場合、リモートエンドによって閉じられているため、接続を離れてスローします。

available()が存在しないため、ほとんどの読み取り試行はすぐに終了し、一部のデータを戻すか、またはスキップしても何も起こりません。もう一方の端が接続を閉じた場合、最後の正常な読み取りのタイムスタンプが古すぎるため、これを1秒以内に検出します。この場合、-1を返す実際の読み取りを実行し、それに応じてソケットのisClosed()が更新されます。ソケットがまだ開いているのにキューが長すぎて遅延が1秒以上ある場合は、接続がまだ存在していても準備ができていないことを確認するために100ミリ秒かかります。

編集:これは、「最後に成功した読み取り」を「最後にブロック読み取り」に変更し、TimeoutExceptionを取得する際にタイムスタンプを更新することです。

3

短い回答:いいえ。より長い答えは、EJPのものを参照してください。

これはおそらく、ソケットをキューに戻すのではなく、ソケットからのすべての要求を処理してから閉じてください。要求を別々に処理するために異なるワーカースレッドに接続を渡しても、あなたには何の利点もありません。

クライアントの動作が悪い場合は、ソケットで読み取りタイムアウトを使用できるため、読み取りはタイムアウトが発生するまでブロックされます。それで、あなたのサーバは、うまく動作しないクライアントに対応する時間がないので、そのソケットを閉じることができます。

2

この問題を解決する方法はありますか?私。切断されたクライアントと受動クライアントを区別することは可能ですか?

ブロッキングIOを使用している場合は実際にはありません。

少し違ったことに対処するノンブロッキング(NIO)パッケージを調べることができます。

本質的に、「セレクタ」に登録できるソケットがあります。 "is data ready for read"のソケットを登録すると、個別にポーリングすることなく読み込むソケットを決定することができます。

書き込みと同じ種類のものです。ここで

tutorial on writing NIO servers

+0

私が正しく理解していれば、IOをブロックすることとは何の関係もありません。問題を解決するためにavailable()という質問が使用されています。むしろ、問題は、クライアントが予期せずシャットダウンしたのか、それとも静かなのかを判断する方法です。 NIOを使うことはこれを助けません。 –

+0

私はより大きなクエリ 'このアプローチの問題:クライアントが何も送信しなくてもスレッドはクライアントによってブロックされます。 'に対処しようとしていました。接続されていないクライアントが両方のモデルに存在しますが、少なくともNIOではそれらを待っていません – ptomli

+0

available()いいえを使用しても同じですか?まだ待っていない。 –

0

ではありません、適切にソケットをシャットダウンしていないクライアントからの非アクティブなクライアントを識別するための唯一の方法は、彼らはまだそこにいるかどうかを確認するには、pingや何かを送信することです。私が見ることができる

可能な解決策は、しばらくの間、何も送信していない

  • キッククライアントです。あなたは静かであったかどうかを把握しなければならず、一度限界に達すると、彼らは切断したとみなします。
  • クライアントにpingがまだ存在するかどうかを確認します。私はあなたに何も送信せずにこれを行う方法を尋ねたことは知っていますが、これが本当に問題であれば、つまり上記の解決策を使用できない場合、これは具体的に応じて行う最良の方法です詳細を想像しなければならないかもしれません)。
  • 両方の組み合わせですが、実際これがおそらく良いでしょう。彼らがまだ生きているかどうかを確認するためにpingを送信した後、彼らが静かであった時間を記録しておいてください。
関連する問題