web-dev-qa-db-ja.com

HttpURLConnectionの実装

HttpURLConnectionは永続的な接続をサポートしているため、接続を複数のリクエストに再利用できることを読みました。私はそれを試しましたが、2番目のPOSTを送信する唯一の方法は、openConnectionをもう一度呼び出すことでした。それ以外の場合はIllegalStateException( "Alreadyconnected");を使用しました。

try{
URL url = new URL("http://someconection.com");
}
catch(Exception e){}
HttpURLConnection con = (HttpURLConnection) url.openConnection();
//set output, input etc
//send POST
//Receive response
//Read whole response
//close input stream
con.disconnect();//have also tested commenting this out
con = (HttpURLConnection) url.openConnection();
//Send new POST

2番目のリクエストは同じTCP接続(wiresharkで確認済み)を介して送信されますが、disconnectを呼び出したため、理由がわかりません(これは私が望むものですが)。ソースコードを確認しましたHttpURLConnectionの場合、実装は同じ宛先への接続のキープアライブキャッシュを保持します。私の問題は、最初のリクエストを送信した後、接続がキャッシュにどのように戻されるかを確認できないことです。切断すると、接続が閉じられます。切断しても、接続がキャッシュにどのように戻されるかはわかりません。キャッシュには、すべてのアイドル状態の接続を通過する実行メソッドがあることがわかりました(どのように呼び出されるかはわかりません)が、見つかりません接続がキャッシュに戻される方法。発生しているように見える唯一の場所はhttpClientのfinishedメソッドですが、これはPOST応答付きで呼び出されません。誰か助けてくれませんか?この?

[〜#〜] edit [〜#〜]私の興味は、tcp接続を再利用するためのHttpUrlConnectionオブジェクトの適切な処理とは何かということです。入力/出力ストリームを閉じてからurl.openConnection();を閉じる必要があります。新しいリクエストを送信するたびに(disconnect()を回避)?はいの場合、最初のリクエストで接続がキャッシュから削除され、どのように返されるかがわからないため、2回目にurl.openConnection()を呼び出したときに接続がどのように再利用されているかを確認できません。接続がキープアライブキャッシュに戻されない可能性はありますか(バグ?)、OSはまだtcp接続を解放しておらず、新しい接続では、OSはバッファリングされた接続(まだ解放されていない)などを返しますか? EDIT2私が見つけた唯一の関連は JDK_KeepAlive からでした

...アプリケーションがURLConnection.getInputStream()によって返されるInputStreamでclose()を呼び出すと、JDKのHTTPプロトコルハンドラーは接続をクリーンアップしようとし、成功した場合は、将来のHTTP要求で再利用できるように接続を接続キャッシュに入れます。 。

しかし、これがどのハンドラーかはわかりません。私が見たように、Sun.net.www.protocol.http.Handlerはキャッシュを行いません。

17
Cratylus

入力/出力ストリームを閉じてからurl.openConnection();を閉じる必要があります。新しいリクエストを送信するたびに(disconnect()を回避)?

はい。

はいの場合、最初のリクエストで接続がキャッシュから削除され、どのように返されるかがわからないため、2回目にurl.openConnection()を呼び出したときに接続がどのように再利用されているかを確認できません。

HttpURLConnectionを基礎となるSocketおよびits基礎となるTCP接続と混同しています。これらは同じではありません。HttpURLConnectionインスタンスはGCされ、disconnect().を呼び出さない限り、基になるSocketはプールされます。

17
user207421

HttpURLConnectionのjavadocから(私の強調):

各HttpURLConnectionインスタンスは、単一のリクエストを作成するために使用されますが、HTTPサーバーへの基盤となるネットワーク接続は他のインスタンスによって透過的に共有される場合があります。 HttpURLConnectionのInputStreamまたはOutputStreamでclose()メソッドを呼び出すリクエストは、このインスタンスに関連付けられているネットワークリソースを解放する場合がありますが、共有の永続的な接続には影響しません。永続的な接続がその時点でアイドル状態である場合は、disconnect()メソッドを呼び出すmay基になるソケットを閉じます。

7
Jim Garrison

InputStreamが閉じられると、接続が実際にキャッシュされることがわかりました。 inputStreamが閉じられると、基になる接続がバッファリングされます。ただし、HttpURLConnectionオブジェクトは、オブジェクトがまだ「接続されている」と見なされるため、以降のリクエストには使用できません。つまり、ブール接続はtrueに設定され、接続がバッファに戻されるとクリアされません。したがって、新しいHttpUrlConnectionをインスタンス化して新しいPOSTを実行するたびに、基になるTCP接続がタイムアウトになっていない場合は再利用されます。したがって、EJPの回答が正しい説明でした。私が見た動作(disconnect()を明示的に呼び出したにもかかわらずTCP接続)を再利用したのは、OSによって行われたキャッシュによるものでしたか?わかりません。知っている人が説明してくれることを願っています。ありがとう。

4
Cratylus

JDKのHttpUrlConnectionを使用して、どのように「HTTP1.0の使用を強制」しますか?

セクションによると Java 1.5ガイド の「持続的接続」== HTTP1.1接続のサポートは、Javaプロパティhttp.keepAlive(デフォルトはtrue)さらに、Javaプロパティhttp.maxConnectionsは、宛先ごとに存続する(同時)接続の最大数を示します。いつでも。

したがって、Javaプロパティhttp.keepAliveをfalseに設定することで、「HTTP1.0の強制使用」をアプリケーション全体に一度に適用できます。

4
deadhorse

うーん。私はここで何かが欠けているかもしれません(これは古い質問なので)、しかし私が知る限り、基礎となるTCP接続を強制的に閉じるには2つのよく知られた方法があります:

  • HTTP 1.0の強制使用(1.1で導入された持続的接続)-これはhttpリクエスト行で示されています
  • 値が「close」の「Connection」ヘッダーを送信します。これにより、強制的に閉じることもできます。
2
StaxMan

ストリームを破棄すると、アイドル状態のTCP接続が発生します。応答ストリームは完全に読み取る必要があります。最初に見落としていたもう1つのことは、このトピックに関するほとんどの回答で見落とされていることです。エラーストリームの処理を忘れています。例外の場合。これに類似したコードは、リソースを適切に解放していなかった私のアプリの1つを修正しました。

HttpURLConnection connection = (HttpURLConnection)new URL(uri).openConnection();
InputStream stream = null;
BufferedReader reader = null;
try {
        stream = connection.getInputStream();
        reader = new BufferedReader(new InputStreamReader(stream, Charset.forName("UTF-8")));

        // do work on part of the input stream

} catch (IOException e) {

    // read the error stream
    InputStream es = connection.getErrorStream();
    if (es != null) {
        BufferedReader esReader = null;
        esReader = new BufferedReader(new InputStreamReader(es, Charset.forName("UTF-8")));
        while (esReader.ready() && esReader.readLine() != null) {
        }
        if (esReader != null)
            esReader.close();
    }

    // do something with the IOException
} finally {

    // finish reading the input stream if it was not read completely in the try block, then close
    if (reader != null) {
        while (reader.readLine() != null) {
        }
        reader.close();
    }

    // Not sure if this is necessary, closing the buffered reader may close the input stream?
    if (stream != null) {
        stream.close();
    }

    // disconnect
    if (connection != null) {
        connection.disconnect();
    }
}

バッファリングされたリーダーは厳密には必要ありません。私のユースケースでは一度に1行ずつ読み取る必要があるため、これを選択しました。

参照: http://docs.Oracle.com/javase/1.5.0/docs/guide/net/http-keepalive.html

0
Crutis