Bisakah Anda menjelaskan proses koneksi HttpURLConnection?


137

Saya menggunakan HTTPURLConnectionuntuk menyambung ke layanan web. Saya tahu cara menggunakan HTTPURLConnectiontetapi saya ingin memahami cara kerjanya. Pada dasarnya, saya ingin mengetahui yang berikut ini:

  • Pada titik manakah HTTPURLConnectionmencoba membuat sambungan ke URL yang diberikan?
  • Pada titik mana saya dapat mengetahui bahwa saya berhasil membuat sambungan?
  • Apakah membuat koneksi dan mengirim permintaan sebenarnya dilakukan dalam satu langkah / panggilan metode? Metode apa itu?
  • Bisakah Anda menjelaskan fungsi getOutputStreamdan getInputStreamdalam istilah awam? Saya perhatikan bahwa ketika server yang saya coba sambungkan turun, saya mendapatkan Exceptiondi getOutputStream. Apakah ini berarti bahwa HTTPURLConnectionhanya akan mulai membuat sambungan saat saya memanggil getOutputStream? Bagaimana dengan getInputStream? Karena saya hanya bisa mendapatkan respons di getInputStream, apakah itu berarti saya belum mengirim permintaan apa getOutputStreampun tetapi hanya membuat sambungan? Apakah HttpURLConnectionkembali ke server untuk meminta tanggapan saat saya memanggil getInputStream?
  • Apakah saya benar mengatakan bahwa openConnectionhanya membuat objek koneksi baru tetapi belum membuat koneksi apa pun?
  • Bagaimana cara mengukur overhead baca dan menghubungkan overhead?

Jawaban:


188
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) {
    // ...
}

3 jawaban pertama atas pertanyaan Anda terdaftar sebagai komentar sebaris, di samping setiap metode, dalam contoh HTTP POST di atas.

Dari getOutputStream :

Mengembalikan aliran keluaran yang menulis ke koneksi ini.

Pada dasarnya, saya pikir Anda memiliki pemahaman yang baik tentang cara kerjanya, jadi izinkan saya mengulanginya dalam istilah awam. getOutputStreampada dasarnya membuka aliran koneksi , dengan tujuan menulis data ke server. Dalam contoh kode di atas, "pesan" bisa menjadi komentar yang kita kirim ke server yang mewakili komentar yang ditinggalkan pada sebuah posting. Saat Anda melihat getOutputStream, Anda membuka aliran koneksi untuk menulis, tetapi Anda tidak benar-benar menulis data apa pun hingga Anda menelepon writer.write("message=" + message);.

Dari getInputStream () :

Mengembalikan aliran input yang membaca dari koneksi terbuka ini. SocketTimeoutException dapat dilemparkan saat membaca dari aliran input yang dikembalikan jika batas waktu baca kedaluwarsa sebelum data tersedia untuk dibaca.

getInputStreammelakukan sebaliknya. Seperti getOutputStream, ini juga membuka aliran koneksi , tetapi tujuannya adalah untuk membaca data dari server, bukan menulis ke sana. Jika koneksi atau pembukaan aliran gagal, Anda akan melihat file SocketTimeoutException.

Bagaimana dengan getInputStream? Karena saya hanya bisa mendapatkan respons di getInputStream, apakah itu berarti saya belum mengirim permintaan apa pun di getOutputStream tetapi hanya membuat koneksi?

Perlu diingat bahwa mengirim permintaan dan mengirim data adalah dua operasi yang berbeda. Saat Anda menjalankan getOutputStream atau getInputStream url.openConnection() , Anda mengirim permintaan ke server untuk membuat sambungan. Ada jabat tangan yang terjadi di mana server mengirimkan kembali pengakuan kepada Anda bahwa koneksi dibuat. Pada saat itulah Anda siap untuk mengirim atau menerima data. Jadi, Anda tidak perlu memanggil getOutputStream untuk membuat koneksi membuka aliran, kecuali tujuan Anda membuat permintaan adalah untuk mengirim data.

Dalam istilah awam, mengajukan getInputStreampermintaan sama dengan menelepon ke rumah teman Anda untuk mengatakan "Hei, bolehkah saya datang dan meminjam sepasang wakil pegangan itu?" dan teman Anda menjalin jabat tangan dengan mengatakan, "Tentu! Datang dan dapatkan". Kemudian, pada titik itu, koneksi dibuat, Anda berjalan ke rumah teman Anda, mengetuk pintu, meminta wakil pegangan, dan berjalan kembali ke rumah Anda.

Menggunakan contoh serupa untuk getOutputStreamakan melibatkan menelepon teman Anda dan berkata "Hei, saya punya uang yang saya berutang kepada Anda, dapatkah saya mengirimkannya kepada Anda"? Teman Anda, yang membutuhkan uang dan sakit hati karena Anda menyimpannya begitu lama, berkata "Tentu, ayolah bajingan murahan". Jadi, Anda berjalan ke rumah teman Anda dan "POSTING" uang itu kepadanya. Dia kemudian menendang Anda keluar dan Anda berjalan kembali ke rumah Anda.

Sekarang, lanjutkan dengan contoh orang awam, mari kita lihat beberapa Pengecualian. Jika Anda menelepon teman Anda dan dia tidak ada di rumah, itu mungkin kesalahan 500. Jika Anda menelepon dan mendapat pesan nomor terputus karena teman Anda bosan dengan Anda meminjam uang sepanjang waktu, halaman 404 itu tidak ditemukan. Jika ponsel Anda mati karena Anda tidak membayar tagihan, itu bisa menjadi IOException. (CATATAN: Bagian ini mungkin tidak 100% benar. Ini dimaksudkan untuk memberi Anda gambaran umum tentang apa yang terjadi dalam istilah awam.)

Pertanyaan # 5:

Ya, Anda benar bahwa openConnection hanya membuat objek koneksi baru tetapi tidak membuatnya. Koneksi dibuat saat Anda memanggil getInputStream atau getOutputStream.

openConnectionmembuat objek koneksi baru. Dari javadocs URL.openConnection :

Koneksi baru dibuka setiap kali dengan memanggil metode openConnection dari penangan protokol untuk URL ini.

Koneksi dibuat saat Anda memanggil openConnection, dan InputStream, OutputStream, atau keduanya, dipanggil saat Anda membuat instance-nya.

Pertanyaan # 6 :

Untuk mengukur overhead, saya biasanya membungkus beberapa kode waktu yang sangat sederhana di seluruh blok koneksi, seperti:

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) );

Saya yakin ada metode yang lebih canggih untuk mengukur waktu permintaan dan overhead, tetapi ini umumnya cukup untuk kebutuhan saya.

Untuk informasi tentang menutup koneksi, yang tidak Anda tanyakan, lihat Di Java kapan koneksi URL ditutup? .


1
Hai. Terima kasih!!! Itu memang penjelasan yang detail dan saya sangat menghargai jawaban Anda. Jika saya memahami jawaban Anda dengan benar, getOutputStream dan getInputStream membuat koneksi jika belum ada koneksi yang dibuat. Jika saya memanggil getOutputStream, lalu memanggil getInputStream, secara internal, HTTPURLConnection tidak akan membangun kembali koneksi di getInputStream lagi karena saya sudah dapat membuatnya di getOutStream? HttpURLConnection akan menggunakan kembali koneksi apa pun yang dapat saya buat di getOutputStream di getInputStream.
Arci

Lanjutan: Atau apakah itu membuat koneksi baru dan terpisah untuk getOutputStream dan getInputStream? Juga, jika saya ingin mendapatkan overhead koneksi, maka tempat yang tepat untuk meletakkan timer saya adalah sebelum dan sesudah getOutputStream. Jika saya ingin mendapatkan overhead baca, maka tempat yang tepat untuk meletakkan timer saya adalah sebelum dan sesudah getInputStream.
Arci

Ingat apa yang dikatakan javadoc tentang getInputStream dan getOutputStream: Returns an output stream that writes to this connection.dan Returns an input stream that reads from this open connection.. Arus keluaran dan arus masukan terpisah dari koneksi.
jmort253

8
Perlu dicatat bahwa tampaknya objek HttpURLConnection hanya menjangkau URL target pada titik di mana ia PERLU melakukannya. Dalam contoh Anda, Anda memiliki aliran input dan output, yang tentu saja tidak dapat melakukan apa pun hingga koneksi terbuka. Kasus yang lebih sederhana adalah operasi GET, di mana Anda tidak melakukan apa-apa selain menginisialisasi koneksi dan kemudian memeriksa kode respons. Dalam hal ini, koneksi tidak benar-benar dibuat hingga metode getResponseCode () dipanggil. Jika tidak, ini adalah penjelasan dan eksplorasi yang bagus tentang siklus hidup koneksi!
Spanky Quigman

1
Saya bingung sebelumnya antara contoh 'UrlConnection' dan koneksi Tcp / Ip / SSL yang mendasarinya, 2 konsep terpisah. Yang pertama pada dasarnya identik dengan satu permintaan halaman HTTP. Yang terakhir adalah sesuatu yang diharapkan akan dibuat sekali hanya jika Anda melakukan banyak permintaan halaman ke server yang sama.
Tim Cooper


1

Pada titik manakah HTTPURLConnection mencoba membuat sambungan ke URL yang diberikan?

Di porta yang disebutkan di URL jika ada, jika tidak 80 untuk HTTP dan 443 untuk HTTPS. Saya yakin ini didokumentasikan.

Pada titik mana saya dapat mengetahui bahwa saya berhasil membuat sambungan?

Saat Anda memanggil getInputStream () atau getOutputStream () atau getResponseCode () tanpa mendapatkan pengecualian.

Apakah membuat koneksi dan mengirim permintaan sebenarnya dilakukan dalam satu langkah / panggilan metode? Metode apa itu?

Tidak dan tidak ada.

Bisakah Anda menjelaskan fungsi getOutputStream dan getInputStream dalam istilah awam?

Salah satunya terhubung terlebih dahulu jika perlu, lalu mengembalikan streaming yang diperlukan.

Saya perhatikan bahwa ketika server yang saya coba sambungkan mati, saya mendapatkan Pengecualian di getOutputStream. Apakah ini berarti HTTPURLConnection hanya akan mulai membuat sambungan saat saya menjalankan getOutputStream? Bagaimana dengan getInputStream? Karena saya hanya bisa mendapatkan respons di getInputStream, apakah itu berarti saya belum mengirim permintaan apa pun di getOutputStream tetapi hanya membuat koneksi? Apakah HttpURLConnection kembali ke server untuk meminta tanggapan ketika saya memanggil getInputStream?

Lihat di atas.

Apakah saya benar untuk mengatakan bahwa openConnection hanya membuat objek koneksi baru tetapi belum membuat koneksi apa pun?

Iya.

Bagaimana cara mengukur overhead baca dan menghubungkan overhead?

Connect: luangkan waktu yang diperlukan getInoutStream () atau getOutputStream () untuk kembali, mana saja yang Anda panggil terlebih dahulu. Baca: waktu dari mulai membaca pertama hingga mendapatkan EOS.


1
Saya pikir OP berarti koneksi titik mana yang dibuat dan pada titik mana kita dapat mengetahui status koneksi. Bukan port url yang terhubung ke. Saya menduga ini diarahkan ke openConnection () dan getInoutStream () / getOutputStream () / getResponseCode () jawabannya nanti.
Aniket Thakur

1

Pada titik manakah HTTPURLConnection mencoba membuat sambungan ke URL yang diberikan?

Perlu diklarifikasi, ada contoh 'UrlConnection' dan kemudian ada koneksi soket Tcp / Ip / SSL yang mendasari , 2 konsep berbeda. Instance 'UrlConnection' atau 'HttpUrlConnection' sama dengan satu permintaan halaman HTTP, dan dibuat saat Anda memanggil url.openConnection (). Tetapi jika Anda melakukan beberapa url.openConnection () dari satu contoh 'url' maka jika Anda beruntung, mereka akan menggunakan kembali soket Tcp / Ip yang sama dan hal-hal handshaking SSL ... yang bagus jika Anda melakukan banyak permintaan halaman ke server yang sama, terutama jika Anda menggunakan SSL di mana biaya pembuatan soket sangat tinggi.

Lihat: Implementasi HttpURLConnection


0

Saya menjalani latihan untuk menangkap pertukaran paket tingkat rendah, dan menemukan bahwa koneksi jaringan hanya dipicu oleh operasi seperti getInputStream, getOutputStream, getResponseCode, getResponseMessage, dll.

Berikut adalah pertukaran paket yang ditangkap ketika saya mencoba menulis program kecil untuk mengunggah file ke Dropbox.

masukkan deskripsi gambar di sini

Di bawah ini adalah program mainan dan penjelasan saya

    /* 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();
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.