HttpURLConnection接続プロセスを説明できますか?


134

私が使っているHTTPURLConnectionWebサービスに接続します。使い方は知っていますHTTPURLConnectionが、仕組みを知りたいです。基本的に、次のことを知りたいです。

  • HTTPURLConnection指定されたURLへの接続を確立しようとするのはどの時点ですか?
  • 接続を正常に確立できたことをどの時点で知ることができますか?
  • 接続の確立と実際のリクエストの送信は、1つのステップ/メソッド呼び出しで行われていますか?どのような方法ですか?
  • あなたはの機能を説明することができますgetOutputStreamし、getInputStream素人の用語では?接続しようとしているサーバーがダウンしていると、Exceptionatが表示されgetOutputStreamます。それはHTTPURLConnection私が呼び出すときだけ接続を確立し始めることを意味しgetOutputStreamますか?どうgetInputStreamですか?私はでしか応答を取得できgetInputStreamないので、getOutputStreamまだ要求を送信しておらず、単に接続を確立しているだけですか?やるHttpURLConnection私は起動時に応答の要求にサーバーに戻るをgetInputStream
  • openConnection新しい接続オブジェクトを作成するだけで、まだ接続を確立していないと言って間違いありませんか?
  • 読み取りオーバーヘッドと接続オーバーヘッドを測定するにはどうすればよいですか?

回答:


184
String message = URLEncoder.encode("my message", "UTF-8");

try {
    // instantiate the URL object with the target URL of the resource to
    // request
    URL url = new URL("http://www.example.com/comment");

    // instantiate the HttpURLConnection with the URL object - A new
    // connection is opened every time by calling the openConnection
    // method of the protocol handler for this URL.
    // 1. This is the point where the connection is opened.
    HttpURLConnection connection = (HttpURLConnection) url
            .openConnection();
    // set connection output to true
    connection.setDoOutput(true);
    // instead of a GET, we're going to send using method="POST"
    connection.setRequestMethod("POST");

    // instantiate OutputStreamWriter using the output stream, returned
    // from getOutputStream, that writes to this connection.
    // 2. This is the point where you'll know if the connection was
    // successfully established. If an I/O error occurs while creating
    // the output stream, you'll see an IOException.
    OutputStreamWriter writer = new OutputStreamWriter(
            connection.getOutputStream());

    // write data to the connection. This is data that you are sending
    // to the server
    // 3. No. Sending the data is conducted here. We established the
    // connection with getOutputStream
    writer.write("message=" + message);

    // Closes this output stream and releases any system resources
    // associated with this stream. At this point, we've sent all the
    // data. Only the outputStream is closed at this point, not the
    // actual connection
    writer.close();
    // if there is a response code AND that response code is 200 OK, do
    // stuff in the first if block
    if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
        // OK

        // otherwise, if any other status code is returned, or no status
        // code is returned, do stuff in the else block
    } else {
        // Server returned HTTP error code.
    }
} catch (MalformedURLException e) {
    // ...
} catch (IOException e) {
    // ...
}

上記のHTTP POSTの例では、質問に対する最初の3つの回答が、各メソッドの横にインラインコメントとしてリストされています。

getOutputStreamから:

この接続に書き込む出力ストリームを返します。

基本的に、あなたはこれがどのように機能するかをよく理解していると思いますので、簡単な言葉で繰り返してみましょう。getOutputStream基本的に、サーバーにデータを書き込むことを目的として、接続ストリームを開きます。上記のコード例では、「メッセージ」は、投稿に残されたコメントを表す、サーバーに送信するコメントである場合があります。を見るとgetOutputStream、書き込みのために接続ストリームを開いていますが、を呼び出すまで、実際にはデータを書き込みませんwriter.write("message=" + message);

getInputStream()から:

この開いている接続から読み取る入力ストリームを返します。データが読み取り可能になる前に読み取りタイムアウトが期限切れになると、返された入力ストリームから読み取るときにSocketTimeoutExceptionがスローされることがあります。

getInputStream反対を行います。と同様にgetOutputStream接続ストリームも開きますが、目的はサーバーからデータを読み取ることであり、サーバーに書き込むことではありません。接続またはストリームを開くことができない場合は、が表示されますSocketTimeoutException

getInputStreamはどうですか?getInputStreamでしか応答を取得できないので、getOutputStreamでまだ要求を送信していないのに、単に接続を確立しているだけですか?

リクエストの送信とデータの送信は2つの異なる操作であることを覚えておいてください。getOutputStreamまたはgetInputStream を呼び出すとurl.openConnection()、接続を確立するためのリクエストがサーバーに送信されます。サーバーが接続が確立されたことの確認を送り返すハンドシェイクがあります。その時点で、データを送受信する準備が整います。したがって、接続確立するために getOutputStreamを呼び出す必要はありません。要求を行う目的がデータの送信である場合を除き、ストリーム開く。

素人の言葉で言えば、 getInputStreamリクエストを行うことは、友人の家に電話をかけて「おい、来てその副グリップを借りても大丈夫ですか?」そして、あなたの友人は、「確かに!是非、それを手に入れなさい」と言って握手を確立します。次に、その時点で接続が確立され、友人の家に歩いてドアをノックし、副グリップを要求して、家に戻ります。

の同様の例を使用するとgetOutputStream、友達に電話して、「おい、私はあなたに借りているお金があります。送金できますか?」あなたの友人は、お金と病気を必要としていて、あなたがそれを長い間それを保ったので、「確かに、あなたに安い野郎をやって来なさい」と言います。それで、あなたはあなたの友人の家に歩いて、彼にお金を「ポスト」します。それから彼はあなたを追い出し、あなたはあなたの家に戻ります。

さて、素人の例を続けて、いくつかの例外を見てみましょう。あなたがあなたの友人に電話をかけたが、彼が家にいなかった場合、それは500エラーの可能性がある。友達がいつもお金を借りるのに飽き飽きしていて、電話が切れて番号メッセージが届いた場合、それは404ページが見つかりません。請求書を支払わなかったためにスマートフォンが死んでいる場合は、IOExceptionである可能性があります。(注:このセクションは100%正確ではない場合があります。これは、素人の言葉で何が起こっているかについての一般的な考えを提供することを目的としています。)

質問5:

はい、あなたは正しいです。openConnectionは単に新しい接続オブジェクトを作成するだけで、確立しません。getInputStreamまたはgetOutputStreamを呼び出すと、接続が確立されます。

openConnection新しい接続オブジェクトを作成します。URL.openConnectionののjavadoc

このURLのプロトコルハンドラーのopenConnectionメソッドを呼び出すたびに、新しい接続が開かれます。

openConnectionを呼び出すと接続が確立され、インスタンス化するときにInputStream、OutputStream、またはその両方が呼び出されます。

質問#6

オーバーヘッドを測定するために、私は通常、次のように、接続ブロック全体を囲む非常に単純なタイミングコードをラップします。

long start = System.currentTimeMillis();
log.info("Time so far = " + new Long(System.currentTimeMillis() - start) );

// run the above example code here
log.info("Total time to send/receive data = " + new Long(System.currentTimeMillis() - start) );

リクエストの時間とオーバーヘッドを測定するためのより高度な方法があると確信していますが、これは通常、私のニーズには十分です。

問い合わせなかった接続のクローズについては、JavaでURL接続はいつクローズするかを参照してください


こんにちは。ありがとう!!! それは確かに詳細な説明であり、私はあなたの答えを本当に感謝しています。私があなたの答えを正しく理解していれば、まだ接続が確立されていない場合、getOutputStreamとgetInputStreamの両方が接続を確立します。getOutputStreamを呼び出してからgetInputStreamを内部的に呼び出すと、すでにgetOutStreamで接続を確立できているため、HTTPURLConnectionはgetInputStreamでの接続を再確立しなくなりますか?HttpURLConnectionは、getInputStreamのgetOutputStreamで確立できた接続を再利用します。
Arci

続き:それとも、getOutputStreamとgetInputStreamのための新しい別の接続を確立しますか?また、接続オーバーヘッドを取得する場合、タイマーを配置する適切な場所はgetOutputStreamの前後です。読み取りオーバーヘッドを取得する場合、タイマーを配置する適切な場所はgetInputStreamの前後です。
Arci

:javadocはのgetInputStreamメソッドおよびgetOutputStreamについて言うことを覚えておいてください Returns an output stream that writes to this connection.Returns an input stream that reads from this open connection.。出力ストリームと入力ストリームは接続から分離されています。
jmort253

8
HttpURLConnectionオブジェクトがターゲットURLに到達する必要がある時点でのみ到達するように見えることは注目に値します。あなたの例では、入力ストリームと出力ストリームがあり、もちろん接続が開かれるまで何もできません。より単純なケースはGET操作で、接続を初期化してから応答コードを確認するだけです。その場合、接続は実際にはgetResponseCode()メソッドが呼び出されるまで行われません。それ以外の場合、これは接続ライフサイクルの優れた説明と調査です。
Spanky Quigman、2013年

1
以前は、「UrlConnection」インスタンスと基になるTcp / Ip / SSL接続の間で混乱していました。2つの異なる概念です。前者は基本的に単一のHTTPページリクエストと同義です。後者は、同じサーバーに対して複数のページリクエストを実行する場合にのみ1回だけ作成されることを期待しています。
Tim Cooper


1

HTTPURLConnectionは、どの時点で指定されたURLへの接続を確立しようとしますか?

URLで指定されているポートがある場合は、HTTPの場合は80、HTTPSの場合は443。これは文書化されていると思います。

接続を正常に確立できたことをどの時点で知ることができますか?

例外を取得せずにgetInputStream()、getOutputStream()、またはgetResponseCode()を呼び出す場合。

接続の確立と実際のリクエストの送信は、1つのステップ/メソッド呼び出しで行われていますか?どのような方法ですか?

いいえ、なし。

getOutputStreamとgetInputStreamの機能を簡単に説明できますか?

どちらかが最初に必要に応じて接続し、次に必要なストリームを返します。

接続しようとしているサーバーがダウンしていると、getOutputStreamで例外が発生します。HTTPURLConnectionは、getOutputStreamを呼び出したときにのみ接続の確立を開始することを意味しますか?getInputStreamはどうですか?getInputStreamでしか応答を取得できないので、getOutputStreamでまだ要求を送信していないのに、単に接続を確立しているだけですか?getInputStreamを呼び出すと、HttpURLConnectionはサーバーに戻って応答を要求しますか?

上記を参照。

openConnectionは単に新しい接続オブジェクトを作成しますが、まだ接続を確立していないと言って間違いありませんか?

はい。

読み取りオーバーヘッドと接続オーバーヘッドを測定するにはどうすればよいですか?

接続:getInoutStream()またはgetOutputStream()が戻るまでの時間のうち、最初に呼び出した方を使用します。読み取り:最初の読み取りを開始してからEOSを取得するまでの時間。


1
OPは接続が確立されるポイントを意味し、その時点で接続ステータスを知ることができると思います。接続先のポートURLではありません。私はこれがopenConnection()とgetInoutStream()/ getOutputStream()/ getResponseCode()に向けられていたと思います。
Aniket Thakur

1

HTTPURLConnectionは、どの時点で指定されたURLへの接続を確立しようとしますか?

明確にする価値があります。「UrlConnection」インスタンスがあり、次に、基礎となるTcp / Ip / SSLソケット接続があります。、2つの異なる概念があります。「UrlConnection」または「HttpUrlConnection」インスタンスは、単一のHTTPページ要求と同義であり、url.openConnection()を呼び出すと作成されます。しかし、1つの 'url'インスタンスから複数のurl.openConnection()を実行すると、運が良ければ、それらは同じTcp / IpソケットとSSLハンドシェイクのものを再利用します...これは、同じサーバーに対して多くのページ要求を行う。特に、ソケットを確立するオーバーヘッドが非常に高いSSLを使用している場合に適しています。

参照: HttpURLConnectionの実装


0

低レベルのパケット交換をキャプチャする演習を行ったところ、ネットワーク接続はgetInputStream、getOutputStream、getResponseCode、getResponseMessageなどの操作によってのみトリガーされることがわかりました。

Dropboxにファイルをアップロードする小さなプログラムを書こうとしたときにキャプチャされたパケット交換は次のとおりです。

ここに画像の説明を入力してください

以下は私のおもちゃプログラムと注釈です

    /* Create a connection LOCAL object,
     * the openConnection() function DOES NOT initiate
     * any packet exchange with the remote server.
     * 
     * The configurations only setup the LOCAL
     * connection object properties.
     */
    HttpURLConnection connection = (HttpURLConnection) dst.openConnection();
    connection.setDoOutput(true);
    connection.setRequestMethod("POST");
    ...//headers setup
    byte[] testContent = {0x32, 0x32};

    /**
     * This triggers packet exchange with the remote
     * server to create a link. But writing/flushing
     * to a output stream does not send out any data.
     * 
     * Payload are buffered locally.
     */
    try (BufferedOutputStream outputStream = new BufferedOutputStream(connection.getOutputStream())) {
        outputStream.write(testContent);
        outputStream.flush();
    }

    /**
     * Trigger payload sending to the server.
     * Client get ALL responses (including response code,
     * message, and content payload) 
     */
    int responseCode = connection.getResponseCode();
    System.out.println(responseCode);

    /* Here no further exchange happens with remote server, since
     * the input stream content has already been buffered
     * in previous step
     */
    try (InputStream is = connection.getInputStream()) {
        Scanner scanner = new Scanner(is);
        StringBuilder stringBuilder = new StringBuilder();
        while (scanner.hasNextLine()) {
        stringBuilder.append(scanner.nextLine()).append(System.lineSeparator());
        }
    }

    /**
     * Trigger the disconnection from the server.
     */
    String responsemsg = connection.getResponseMessage();
    System.out.println(responsemsg);
    connection.disconnect();
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.