Bạn có thể giải thích quá trình kết nối httpURLCconnectection không?


134

Tôi đang sử dụng HTTPURLConnectionđể kết nối với một dịch vụ web. Tôi biết cách sử dụng HTTPURLConnectionnhưng tôi muốn hiểu làm thế nào nó hoạt động. Về cơ bản, tôi muốn biết những điều sau đây:

  • Tại điểm nào HTTPURLConnectioncố gắng thiết lập kết nối với URL đã cho?
  • Ở điểm nào tôi có thể biết rằng tôi có thể thiết lập kết nối thành công?
  • Đang thiết lập kết nối và gửi yêu cầu thực tế được thực hiện trong một bước gọi / phương thức? Đó là phương pháp gì?
  • Bạn có thể giải thích chức năng của getOutputStreamgetInputStreamtrong nhiệm kỳ của giáo dân? Tôi nhận thấy rằng khi máy chủ tôi đang cố gắng kết nối bị hỏng, tôi nhận được Exceptiontại getOutputStream. Có nghĩa là nó HTTPURLConnectionsẽ chỉ bắt đầu thiết lập kết nối khi tôi gọi getOutputStream? Thế còn getInputStream? Vì tôi chỉ có thể nhận được phản hồi tại getInputStream, vậy có nghĩa là tôi chưa gửi bất kỳ yêu cầu getOutputStreamnào mà chỉ thiết lập kết nối? Làm HttpURLConnectionlại đi đến máy chủ để yêu cầu trả lời khi tôi gọi getInputStream?
  • Tôi có đúng không khi nói rằng openConnectionchỉ cần tạo một đối tượng kết nối mới nhưng chưa thiết lập bất kỳ kết nối nào?
  • Làm thế nào tôi có thể đo chi phí đọc và kết nối trên không?

Câu trả lời:


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

3 câu trả lời đầu tiên cho câu hỏi của bạn được liệt kê dưới dạng nhận xét nội tuyến, bên cạnh mỗi phương thức, trong ví dụ HTTP POST ở trên.

Từ getOutputStream :

Trả về một luồng đầu ra ghi vào kết nối này.

Về cơ bản, tôi nghĩ rằng bạn hiểu rõ về cách thức hoạt động của nó, vì vậy hãy để tôi nhắc lại trong các điều khoản của giáo dân. getOutputStreamvề cơ bản sẽ mở ra một luồng kết nối , với ý định ghi dữ liệu lên máy chủ. Trong ví dụ mã ở trên, "tin nhắn" có thể là một bình luận mà chúng tôi đang gửi đến máy chủ đại diện cho một bình luận để lại trên một bài đăng. Khi bạn thấy getOutputStream, bạn đang mở luồng kết nối để viết, nhưng thực tế bạn không viết bất kỳ dữ liệu nào cho đến khi bạn gọi writer.write("message=" + message);.

Từ getInputStream () :

Trả về một luồng đầu vào đọc từ kết nối mở này. Có thể ném một SocketTimeoutException khi đọc từ luồng đầu vào được trả về nếu hết thời gian đọc trước khi dữ liệu có sẵn để đọc.

getInputStreamlàm ngược lại. Giống như getOutputStream, nó cũng mở một luồng kết nối , nhưng mục đích là đọc dữ liệu từ máy chủ, không ghi vào nó. Nếu kết nối hoặc mở luồng không thành công, bạn sẽ thấy a SocketTimeoutException.

Làm thế nào về getInputStream? Vì tôi chỉ có thể nhận được phản hồi tại getInputStream, nên điều đó có nghĩa là tôi chưa gửi bất kỳ yêu cầu nào tại getOutputStream mà chỉ thiết lập kết nối?

Hãy nhớ rằng gửi yêu cầu và gửi dữ liệu là hai hoạt động khác nhau. Khi bạn gọi getOutputStream hoặc getInputStream url.openConnection() , bạn gửi yêu cầu đến máy chủ để thiết lập kết nối. Có một cái bắt tay xảy ra trong đó máy chủ gửi lại một xác nhận cho bạn rằng kết nối được thiết lập. Đó là lúc bạn chuẩn bị gửi hoặc nhận dữ liệu. Do đó, bạn không cần gọi getOutputStream để thiết lập kết nối mở một luồng, trừ khi mục đích của bạn để thực hiện yêu cầu là gửi dữ liệu.

Theo cách nói của giáo dân, làm cho một getInputStream yêu cầu tương đương với việc gọi điện thoại đến nhà bạn của bạn để nói "Này, có ổn không nếu tôi ghé qua và mượn cặp phó kẹp đó?" và bạn của bạn thiết lập cái bắt tay bằng cách nói, "Chắc chắn! Hãy đến và lấy nó". Sau đó, tại thời điểm đó, kết nối được thực hiện, bạn đi bộ đến nhà của bạn mình, gõ cửa, yêu cầu các phó kẹp và quay trở lại nhà của bạn.

Sử dụng một ví dụ tương tự getOutputStreamsẽ liên quan đến việc gọi cho bạn của bạn và nói "Này, tôi có số tiền đó tôi nợ bạn, tôi có thể gửi nó cho bạn không"? Bạn của bạn, cần tiền và bệnh tật bên trong mà bạn đã giữ nó quá lâu, nói "Chắc chắn, đến với bạn khốn rẻ tiền". Vì vậy, bạn đi bộ đến nhà của bạn bè của bạn và "ĐĂNG" tiền cho anh ta. Sau đó anh ta đuổi bạn ra ngoài và bạn trở về nhà.

Bây giờ, tiếp tục với ví dụ của giáo dân, chúng ta hãy xem xét một số Ngoại lệ. Nếu bạn gọi cho bạn của bạn và anh ấy không ở nhà, đó có thể là một lỗi 500. Nếu bạn gọi và nhận được một tin nhắn số bị ngắt kết nối vì bạn của bạn mệt mỏi vì bạn vay tiền mọi lúc, đó không phải là trang 404. Nếu điện thoại của bạn chết vì bạn không thanh toán hóa đơn, đó có thể là IOException. (LƯU Ý: Phần này có thể không chính xác 100%. Nó nhằm cung cấp cho bạn ý tưởng chung về những gì đang xảy ra trong các điều khoản của giáo dân.)

Câu hỏi số 5:

Đúng, bạn đúng rằng openConnection chỉ đơn giản là tạo một đối tượng kết nối mới nhưng không thiết lập nó. Kết nối được thiết lập khi bạn gọi getInputStream hoặc getOutputStream.

openConnectiontạo một đối tượng kết nối mới. Từ URL.openConnection javadocs :

Một kết nối mới được mở mỗi lần bằng cách gọi phương thức openConnection của trình xử lý giao thức cho URL này.

Kết nối được thiết lập khi bạn gọi openConnection và InputStream, OutputStream hoặc cả hai, được gọi khi bạn khởi tạo chúng.

Câu hỏi số 6 :

Để đo chi phí, tôi thường bọc một số mã thời gian rất đơn giản xung quanh toàn bộ khối kết nối, như vậy:

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

Tôi chắc chắn có nhiều phương pháp nâng cao hơn để đo thời gian và chi phí yêu cầu, nhưng điều này thường đủ cho nhu cầu của tôi.

Để biết thông tin về việc đóng các kết nối mà bạn không hỏi về, hãy xem trong Java khi nào kết nối URL đóng? .


Chào. Cảm ơn!!! Đó thực sự là một lời giải thích chi tiết và tôi thực sự đánh giá cao câu trả lời của bạn. Nếu tôi hiểu chính xác câu trả lời của bạn, cả getOutputStream và getInputStream đều thiết lập kết nối nếu chưa có kết nối nào được thiết lập. Nếu tôi gọi getOutputStream, sau đó gọi getInputStream, trong nội bộ, HTTPURLC Connectection sẽ không thiết lập lại kết nối tại getInputStream nữa vì tôi đã có thể thiết lập nó tại getOutStream? HttpURLConnection sẽ sử dụng lại bất kỳ kết nối nào tôi có thể thiết lập tại getOutputStream trong getInputStream.
Arci

Tiếp: Hoặc nó thiết lập một kết nối mới và riêng biệt cho getOutputStream và getInputStream? Ngoài ra, nếu tôi muốn có được kết nối trên đầu, thì vị trí thích hợp để đặt bộ hẹn giờ của tôi là trước và sau getOutputStream. Nếu tôi muốn có được chi phí đọc, thì vị trí thích hợp để đặt bộ hẹn giờ của tôi là trước và sau getInputStream.
Arci

Hãy nhớ những gì javadoc nói về getInputStream và getOutputStream: Returns an output stream that writes to this connection.Returns an input stream that reads from this open connection.. Đầu ra và đầu vào tách biệt với kết nối.
jmort253

8
Điều đáng chú ý là có vẻ như đối tượng httpURLConnection chỉ tiếp cận với URL mục tiêu tại điểm mà nó CẦN phải làm như vậy. Trong ví dụ của bạn, bạn có các luồng đầu vào và đầu ra, tất nhiên không thể làm gì cho đến khi kết nối được mở. Một trường hợp đơn giản hơn nhiều là thao tác GET, trong đó bạn không làm gì ngoài việc khởi tạo kết nối và sau đó kiểm tra mã phản hồi. Trong trường hợp đó, kết nối không thực sự được thực hiện cho đến khi phương thức getResponseCode () được gọi. Mặt khác, đây là một lời giải thích và khám phá tuyệt vời về vòng đời kết nối!
Climby Quigman

1
Tôi đã nhầm lẫn trước đây giữa trường hợp 'UrlConnection' và kết nối Tcp / Ip / SSL cơ bản, 2 khái niệm riêng biệt. Cái trước về cơ bản là đồng nghĩa với một yêu cầu trang HTTP. Cái sau là thứ gì đó hy vọng sẽ được tạo một lần chỉ khi bạn thực hiện nhiều yêu cầu trang đến cùng một máy chủ.
Tim Cooper


1

HTTPURLCconnectection cố gắng thiết lập kết nối với URL đã cho vào thời điểm nào?

Trên cổng có tên trong URL nếu có, nếu không, 80 cho HTTP và 443 cho HTTPS. Tôi tin rằng đây là tài liệu.

Ở điểm nào tôi có thể biết rằng tôi có thể thiết lập kết nối thành công?

Khi bạn gọi getInputStream () hoặc getOutputStream () hoặc getResponseCode () mà không gặp ngoại lệ.

Đang thiết lập kết nối và gửi yêu cầu thực tế được thực hiện trong một bước gọi / phương thức? Đó là phương pháp gì?

Không và không có.

Bạn có thể giải thích chức năng của getOutputStream và getInputStream trong thuật ngữ của cư sĩ không?

Một trong hai kết nối đầu tiên nếu cần thiết, sau đó trả về luồng yêu cầu.

Tôi nhận thấy rằng khi máy chủ tôi đang cố gắng kết nối bị hỏng, tôi sẽ có Ngoại lệ tại getOutputStream. Điều đó có nghĩa là HTTPURLC Connectection sẽ chỉ bắt đầu thiết lập kết nối khi tôi gọi getOutputStream? Làm thế nào về getInputStream? Vì tôi chỉ có thể nhận được phản hồi tại getInputStream, nên điều đó có nghĩa là tôi chưa gửi bất kỳ yêu cầu nào tại getOutputStream mà chỉ thiết lập kết nối? Do HTTPURLConnection quay lại máy chủ để yêu cầu phản hồi khi tôi gọi getInputStream?

Xem ở trên.

Tôi có đúng không khi nói rằng openConnection chỉ đơn giản là tạo một đối tượng kết nối mới nhưng chưa thiết lập bất kỳ kết nối nào?

Đúng.

Làm thế nào tôi có thể đo chi phí đọc và kết nối trên không?

Kết nối: mất thời gian getInoutStream () hoặc getOutputStream () sẽ quay trở lại, bất cứ khi nào bạn gọi trước. Đọc: thời gian từ khi bắt đầu đọc lần đầu tiên đến khi nhận được EOS.


1
Tôi nghĩ OP có nghĩa là kết nối điểm nào được thiết lập và tại điểm nào chúng ta có thể biết trạng thái kết nối. Không phải url cổng kết nối với. Tôi đoán điều này được hướng tới openConnection () và getInoutStream () / getOutputStream () / getResponseCode () câu trả lời sau.
Aniket Thakur

1

HTTPURLCconnectection cố gắng thiết lập kết nối với URL đã cho vào thời điểm nào?

Thật đáng để làm rõ, có trường hợp 'UrlConnection' và sau đó có kết nối ổ cắm Tcp / Ip / SSL bên dưới , 2 khái niệm khác nhau. Ví dụ 'UrlConnection' hoặc 'HttpUrlConnection' đồng nghĩa với một yêu cầu trang HTTP duy nhất và được tạo khi bạn gọi url.openConnection (). Nhưng nếu bạn thực hiện nhiều url.openConnection () từ một ví dụ 'url' thì nếu bạn may mắn, họ sẽ sử dụng lại cùng một ổ cắm Tcp / Ip và công cụ bắt tay SSL ... sẽ tốt nếu bạn thực hiện nhiều yêu cầu trang đến cùng một máy chủ, đặc biệt tốt nếu bạn đang sử dụng SSL trong đó chi phí thiết lập ổ cắm rất cao.

Xem: Thực hiện kết nối httpURLC


0

Tôi đã thực hiện bài tập để nắm bắt trao đổi gói ở mức độ thấp và thấy rằng kết nối mạng chỉ được kích hoạt bởi các hoạt động như getInputStream, getOutputStream, getResponseCode, getResponseMessage, v.v.

Đây là trao đổi gói được chụp khi tôi cố gắng viết một chương trình nhỏ để tải tệp lên Dropbox.

nhập mô tả hình ảnh ở đây

Dưới đây là chương trình đồ chơi và chú thích của tôi

    /* 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();
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.