คุณสามารถอธิบายกระบวนการเชื่อมต่อ HttpURLConnection ได้หรือไม่?


137

ฉันกำลังใช้HTTPURLConnectionเพื่อเชื่อมต่อกับบริการเว็บ ฉันรู้วิธีใช้HTTPURLConnectionแต่ฉันอยากเข้าใจว่ามันทำงานอย่างไร โดยทั่วไปฉันต้องการทราบสิ่งต่อไปนี้:

  • จุดใดที่HTTPURLConnectionพยายามสร้างการเชื่อมต่อกับ URL ที่ระบุ
  • จุดใดที่ฉันสามารถรู้ได้ว่าฉันสามารถสร้างการเชื่อมต่อได้สำเร็จ
  • กำลังสร้างการเชื่อมต่อและส่งคำขอจริงในขั้นตอนเดียว / วิธีการโทรหรือไม่? วิธีการคืออะไร?
  • คุณสามารถอธิบายการทำงานของgetOutputStreamและgetInputStreamในระยะของคนธรรมดาได้หรือไม่? ฉันสังเกตเห็นว่าเมื่อเซิร์ฟเวอร์ที่ฉันพยายามเชื่อมต่อหยุดทำงานฉันจะได้รับExceptionที่getOutputStream. หมายความว่าHTTPURLConnectionจะเริ่มสร้างการเชื่อมต่อเมื่อฉันเรียกใช้เท่านั้นgetOutputStream? แล้วgetInputStreamล่ะ? เนื่องจากฉันสามารถรับคำตอบได้ที่getInputStreamนั่นหมายความว่าฉันยังไม่ได้ส่งคำขอใด ๆgetOutputStreamเลย แต่เพียงแค่สร้างการเชื่อมต่อ ทำHttpURLConnectionกลับไปยังเซิร์ฟเวอร์เพื่อร้องขอสำหรับการตอบสนองเมื่อฉันเรียกgetInputStream?
  • ฉันถูกต้องหรือไม่ที่จะบอกว่าopenConnectionสร้างวัตถุเชื่อมต่อใหม่ แต่ยังไม่ได้สร้างการเชื่อมต่อ
  • ฉันจะวัดค่าใช้จ่ายในการอ่านและเชื่อมต่อค่าโสหุ้ยได้อย่างไร

คำตอบ:


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 ข้อแรกสำหรับคำถามของคุณจะแสดงเป็นความคิดเห็นแบบอินไลน์ข้างแต่ละวิธีในตัวอย่าง HTTP POST ด้านบน

จากgetOutputStream :

ส่งคืนเอาต์พุตสตรีมที่เขียนไปยังการเชื่อมต่อนี้

โดยพื้นฐานแล้วฉันคิดว่าคุณมีความเข้าใจดีเกี่ยวกับวิธีการทำงานดังนั้นขอฉันย้ำอีกครั้งในแง่ของคนธรรมดา getOutputStreamโดยทั่วไปจะเปิดสตรีมการเชื่อมต่อโดยมีจุดประสงค์เพื่อเขียนข้อมูลไปยังเซิร์ฟเวอร์ ในตัวอย่างโค้ดด้านบน "ข้อความ" อาจเป็นความคิดเห็นที่เราส่งไปยังเซิร์ฟเวอร์ซึ่งแสดงถึงความคิดเห็นที่ทิ้งไว้ในโพสต์ เมื่อคุณเห็นgetOutputStreamว่าคุณกำลังเปิดการเชื่อมต่อกระแสสำหรับการเขียน แต่คุณไม่จริงเขียนข้อมูลใด ๆ writer.write("message=" + message);จนกว่าคุณเรียก

จากgetInputStream () :

ส่งคืนอินพุตสตรีมที่อ่านจากการเชื่อมต่อแบบเปิดนี้ SocketTimeoutException สามารถโยนได้เมื่ออ่านจากสตรีมอินพุตที่ส่งคืนหากหมดเวลาการอ่านก่อนที่ข้อมูลจะพร้อมสำหรับการอ่าน

getInputStreamตรงกันข้าม เช่นเดียวกับgetOutputStreamมันยังเปิดสตรีมการเชื่อมต่อแต่มีจุดประสงค์เพื่ออ่านข้อมูลจากเซิร์ฟเวอร์ไม่ใช่เขียนลงไป หากการเชื่อมต่อหรือการเปิดสตรีมล้มเหลวคุณจะเห็นไฟล์SocketTimeoutException.

แล้ว getInputStream ล่ะ? เนื่องจากฉันสามารถรับคำตอบได้ที่ getInputStream นั่นหมายความว่าฉันยังไม่ได้ส่งคำขอใด ๆ ที่ getOutputStream แต่เพียงแค่สร้างการเชื่อมต่อ

โปรดทราบว่าการส่งคำขอและการส่งข้อมูลเป็นการดำเนินการสองอย่างที่แตกต่างกัน เมื่อคุณเรียกใช้getOutputStream หรือ getInputStream url.openConnection()คุณจะส่งคำร้องขอไปยังเซิร์ฟเวอร์เพื่อสร้างการเชื่อมต่อ มีการจับมือเกิดขึ้นโดยที่เซิร์ฟเวอร์ส่งการตอบรับกลับมาให้คุณทราบว่ามีการเชื่อมต่อแล้ว เมื่อถึงเวลานั้นคุณก็พร้อมที่จะส่งหรือรับข้อมูล ดังนั้นคุณไม่จำเป็นต้องเรียกใช้ getOutputStream เพื่อสร้างการเชื่อมต่อเพื่อเปิดสตรีมเว้นแต่จุดประสงค์ของคุณในการส่งคำขอคือการส่งข้อมูล

ในแง่ของคนธรรมดาการgetInputStreamร้องขอจะเทียบเท่ากับการโทรศัพท์ไปบ้านเพื่อนเพื่อบอกว่า "เฮ้ฉันจะมาขอยืมตัวรองคู่นั้นได้ไหม" และเพื่อนของคุณก็เริ่มจับมือกันโดยพูดว่า "ได้สิมารับสิ" จากนั้นเมื่อถึงจุดนั้นคุณก็เดินไปที่บ้านของเพื่อนเคาะประตูขอที่รองจับแล้วเดินกลับไปที่บ้านของคุณ

การใช้ตัวอย่างที่คล้ายกันเพื่อgetOutputStreamโทรหาเพื่อนของคุณและพูดว่า "เฮ้ฉันมีเงินเป็นหนี้คุณฉันจะส่งให้คุณได้ไหม" เพื่อนของคุณต้องการเงินและป่วยอยู่ข้างในที่คุณเก็บมันมานานบอกว่า "ได้สิมาหาไอ้เลว" คุณจึงเดินไปที่บ้านของเพื่อนและ "โพสต์" เงินให้เขา จากนั้นเขาก็ไล่คุณออกและคุณก็เดินกลับไปที่บ้านของคุณ

ตอนนี้ดำเนินการต่อด้วยตัวอย่างของฆราวาสมาดูข้อยกเว้นบางประการ ถ้าคุณโทรหาเพื่อนแล้วเขาไม่อยู่บ้านนั่นอาจเป็นข้อผิดพลาด 500 หากคุณโทรหาและได้รับข้อความหมายเลขที่ถูกตัดการเชื่อมต่อเนื่องจากเพื่อนของคุณเบื่อที่คุณจะยืมเงินตลอดเวลานั่นคือหน้า 404 ไม่พบ หากโทรศัพท์ของคุณเสียเพราะคุณไม่ได้จ่ายบิลนั่นอาจเป็น IOException (หมายเหตุ: ส่วนนี้อาจไม่ถูกต้อง 100% มีจุดมุ่งหมายเพื่อให้คุณทราบโดยทั่วไปว่าเกิดอะไรขึ้นในแง่ของคนธรรมดา)

คำถาม # 5:

ใช่คุณเข้าใจถูกแล้วที่ openConnection เพียงสร้างออบเจ็กต์การเชื่อมต่อใหม่ แต่ไม่ได้สร้าง การเชื่อมต่อถูกสร้างขึ้นเมื่อคุณเรียกใช้ getInputStream หรือ getOutputStream

openConnectionสร้างวัตถุการเชื่อมต่อใหม่ จากURL.openConnection javadocs :

การเชื่อมต่อใหม่จะเปิดขึ้นทุกครั้งโดยเรียกเมธอด openConnection ของตัวจัดการโปรโตคอลสำหรับ URL นี้

การเชื่อมต่อจะถูกสร้างขึ้นเมื่อคุณเรียกใช้ 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 ปิด? .


1
สวัสดี. ขอบคุณ !!! นั่นเป็นคำอธิบายโดยละเอียดและฉันขอขอบคุณสำหรับคำตอบของคุณ หากฉันเข้าใจคำตอบของคุณถูกต้องทั้ง getOutputStream และ getInputStream จะสร้างการเชื่อมต่อหากยังไม่มีการสร้างการเชื่อมต่อ ถ้าฉันเรียก getOutputStream จากนั้นเรียก getInputStream ภายใน HTTPURLConnection จะไม่สร้างการเชื่อมต่อใหม่ที่ getInputStream อีกต่อไปเนื่องจากฉันสามารถสร้างได้ที่ getOutStream? HttpURLConnection จะใช้การเชื่อมต่อที่ฉันสามารถสร้างได้ที่ getOutputStream ใน getInputStream
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

1
ก่อนหน้านี้ฉันสับสนระหว่างอินสแตนซ์ 'UrlConnection' และการเชื่อมต่อ Tcp / Ip / SSL พื้นฐาน 2 แนวคิดแยกกัน ก่อนหน้านี้มีความหมายเหมือนกันกับคำขอหน้า HTTP เดียว สิ่งหลังนี้เป็นสิ่งที่หวังว่าจะถูกสร้างขึ้นเพียงครั้งเดียวหากคุณส่งคำขอหลายหน้าไปยังเซิร์ฟเวอร์เดียวกัน
Tim Cooper

17

Tim Bray นำเสนอแบบกระชับทีละขั้นตอนโดยระบุว่า openConnection () ไม่ได้สร้างการเชื่อมต่อจริง แต่จะไม่มีการสร้างการเชื่อมต่อ HTTP จริงจนกว่าคุณจะเรียกใช้เมธอดเช่น getInputStream () หรือ getOutputStream ()

http://www.tbray.org/ongoing/When/201x/2012/01/17/HttpURLConnection


1

HTTPURLConnection พยายามสร้างการเชื่อมต่อกับ URL ที่กำหนดจุดใด

บนพอร์ตที่ตั้งชื่อใน URL ถ้ามีหรือ 80 สำหรับ HTTP และ 443 สำหรับ HTTPS ฉันเชื่อว่านี่เป็นเอกสาร

จุดใดที่ฉันสามารถรู้ได้ว่าฉันสามารถสร้างการเชื่อมต่อได้สำเร็จ

เมื่อคุณเรียกใช้ getInputStream () หรือ getOutputStream () หรือ getResponseCode () โดยไม่ได้รับข้อยกเว้น

กำลังสร้างการเชื่อมต่อและส่งคำขอจริงในขั้นตอนเดียว / วิธีการโทรหรือไม่? วิธีการคืออะไร?

ไม่ใช่และไม่มี

คุณสามารถอธิบายฟังก์ชันของ getOutputStream และ getInputStream ในรูปแบบของคนธรรมดาได้หรือไม่?

ก่อนอื่นให้เชื่อมต่อหากจำเป็นจากนั้นส่งคืนสตรีมที่ต้องการ

ฉันสังเกตเห็นว่าเมื่อเซิร์ฟเวอร์ที่ฉันพยายามเชื่อมต่อหยุดทำงานฉันจะได้รับ Exception ที่ getOutputStream หมายความว่า HTTPURLConnection จะเริ่มสร้างการเชื่อมต่อเมื่อฉันเรียกใช้ getOutputStream หรือไม่ แล้ว getInputStream ล่ะ? เนื่องจากฉันสามารถรับคำตอบได้ที่ getInputStream นั่นหมายความว่าฉันยังไม่ได้ส่งคำขอใด ๆ ที่ getOutputStream แต่เพียงแค่สร้างการเชื่อมต่อ HttpURLConnection กลับไปที่เซิร์ฟเวอร์เพื่อขอการตอบกลับเมื่อฉันเรียกใช้ getInputStream หรือไม่

ดูด้านบน.

ฉันพูดถูกหรือไม่ว่า 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 () แต่ถ้าคุณทำหลาย url.openConnection () จากอินสแตนซ์ 'url' อันเดียวถ้าคุณโชคดีพวกเขาจะใช้ซ็อกเก็ต 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.