2012-06-27 21 views
9

Android(2.3.x)でThreadSafeClientConnManagerDefaultHttpClientを使用して、自分のRESTサーバー(埋め込みJetty)にHTTPリクエストを送信しています。DefaultHttpClientがハーフクローズソケットでデータを送信するのはなぜですか?

アイドル時間が200秒後に、サーバーはTCP接続を[FIN]で閉じます。 Androidクライアントは[ACK]で応答します。これはソケットを半閉じた状態(サーバはまだリスンしていますが、データを送信することはできません)のままにしておきます。

クライアントがその接続を(HttpClient.execute経由で)再度使用しようとすると、DefaultHttpClientはハーフクローズ状態を検出し、クライアント側のソケットを閉じます(したがって、[FIN/ACK]を送信して閉じる)し、要求の新しい接続を開きます。しかし、擦れがあります。

代わりに、ハーフクローズソケットを介して新しいHTTP要求を送信します。送信後のみハーフクローズ状態が検出され、ソケットはクライアント側で閉じられます([FIN]がサーバーに送信されます)。もちろん、サーバーは要求に応答できません(既に[FIN]を送信していた)ので、クライアントは要求が失敗したと判断し、新しいソケット/接続を介して自動的に再試行します。

最終的に、サーバーは2つの要求のコピーを見て処理します。

これを修正する方法についてのご意見はありますか? (私のサーバーは2番目のコピーで正しいことを行いますが、ペイロードが2回送信されるのは面倒です)

DefaultHttpClientは、新しいHTTPパケットの書き込みを最初に試みたときにソケットが閉じられたことを検出しません。そのソケットをすぐに閉じて、新しいソケットを開始しますか?私は、サーバーが[FIN]を送信した後、新しいHTTPリクエストがソケットでどのように送信されるのか、困惑しています。

+0

'AndroidHttpClient'がまったく同じ振る舞い、おそらくあなたはそれを使用して試すことができます...(' ThreadSafeClientConnManager'と、すなわち 'DefaultHttpClient'は、スレッドの安全性を確保するために)ん?私は、ソケット接続の仕組みについて詳しくは分かりませんが、ちょっとした提案があります。 –

答えて

5

これは、JavaでのブロックI/Oの一般的な制限です。反対側のエンドポイントがソケットからの読み取りを試みる以外の方法で接続を閉じているかどうかを調べる方法はありません。 Apache HttpClientは、本質的に非常に短い読み取り操作である古い接続チェックを採用することで、この問題を回避します。ただし、チェックは無効になり、しばしば無効になります。実際には、チェックがもたらす余分な待ち時間のために無効にすることをお勧めします。私は、Androidに同梱されているHttpClientのバージョンがこの点でどのように動作するのか正確にはわかりませんが、適切なconfigパラメータを使用して明示的にチェックを有効にすることができます。

この問題を解決するには、一定期間使用しないと特定の期間(たとえば150秒)にわたってアイドル状態にあった接続プールからの接続を回避する方がよいでしょう。

http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d5e652

+0

ありがとうございます。無効な接続チェックが有効になっていますが、まだAndroidではチェックが行われていません。これは、スタンドアロンのバニラHttpClient jarを使用して実行されます。おそらくAndroidチームは、Androidレポジトリにインポートしたときに、HttpClientの内部が本当にうんざりしていたかもしれません。私は、アイドル状態の接続の退去スレッドの提案を取ると思います。 –

+1

@David B. GoogleがAdnroidに同梱するのは、非常に古くなった(BETA 4.0より前の)バージョンのApache HttpClientに基づくフォークです。 Googleのエンジニアが古い接続チェックを削除した可能性があります。 – oleg

関連する問題