17

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

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 のソース コードを確認したところ、実装によって同じ宛先への接続のキープアライブ キャッシュが保持されます。私の問題は、最初のリクエストを送信した後、接続がどのようにキャッシュに戻されるかを確認できないことです。切断すると接続が閉じられますが、切断がなければ、接続がキャッシュにどのように戻されるかわかりません。キャッシュにはすべてのアイドル接続を通過する run メソッドがあることがわかりました (どのように呼び出されるかはわかりません) が、接続がキャッシュに戻される方法がわかりません。発生しているように見える唯一の場所は 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 はキャッシュを行いません ありがとう!

4

6 に答える 6

17

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

はい。

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

を基になるTCP接続およびその基になるTCP接続と混同しHttpURLConnectionています。それらは同じではありません。あなたが呼び出さない限り、インスタンスはGCされ、原資産はプールされますSocketHttpURLConnectionSocketdisconnect().

于 2010-08-12T01:41:42.717 に答える
7

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

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

于 2010-08-11T18:23:42.850 に答える
4

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

于 2010-08-12T12:09:46.337 に答える
4

JDKのHttpUrlConnectionを使用して「HTTP1.0の使用を強制」するにはどうすればよいですか?

Java 1.5 ガイドのセクション「Persistent Connections」によると、 HTTP1.1 接続のサポートは、Java プロパティを使用してオフまたはオンにすることができますhttp.keepAlive(デフォルトは true)。さらに、java プロパティhttp.maxConnectionsは、任意の時点で維持される宛先ごとの (同時) 接続の最大数を示します。

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

于 2011-05-13T13:52:53.897 に答える
2

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

  • HTTP 1.0 (1.1 で永続的な接続が導入されました) の使用を強制します -- これは、http 要求行で示されます。
  • 'close' の値で 'Connection' ヘッダーを送信します。これも強制的に閉じます。
于 2010-10-20T16:01:48.150 に答える
0

ストリームを放棄すると、アイドル状態の 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

于 2012-11-13T23:38:16.250 に答える