[java] HttpURLConnection 연결 프로세스를 설명 할 수 있습니까?

HTTPURLConnection웹 서비스에 연결하는 데 사용 하고 있습니다. 사용 방법을 알고 HTTPURLConnection있지만 작동 방식을 이해하고 싶습니다. 기본적으로 다음을 알고 싶습니다.

  • 어느 시점 HTTPURLConnection에서 주어진 URL에 연결을 시도합니까?
  • 어느 시점에서 연결을 성공적으로 설정할 수 있었는지 알 수 있습니까?
  • 한 단계 / 방법 호출로 연결을 설정하고 실제 요청을 전송합니까? 어떤 방법입니까?
  • 평신도의 기능 getOutputStream과 기능을 설명 할 수 있습니까 getInputStream? 연결하려는 서버가 다운되면 Exceptionat을 얻 습니다 getOutputStream. HTTPURLConnection내가 호출 할 때만 연결을 시작 한다는 의미 getOutputStream입니까? 어때요 getInputStream? 에서 응답 만받을 수 있기 때문에 아직 getInputStream요청을 보내지 getOutputStream않고 단순히 연결을 설정 한다는 의미 입니까? 수행 HttpURLConnection내가 호출 할 때 응답을 요청 서버로 돌아 가기 getInputStream?
  • openConnection단순히 새 연결 개체를 만들지 만 아직 연결을 설정하지 않았다고 말하는 것이 맞 습니까?
  • 읽기 오버 헤드 및 연결 오버 헤드를 어떻게 측정 할 수 있습니까?


답변

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하여 친구에게 전화를 걸어 “이봐, 내가 돈을 빚지고있다, 그것을 보낼 수 있니?” 돈이 필요하고 몸이 아파서 오랫동안 오래 간직한 친구는 “물론, 싸구려 놈 이여.” 그래서 당신은 당신의 친구의 집으로 걸어 돈을 그에게 “POST”. 그는 당신을 쫓아 내고 집으로 돌아갑니다.

이제 평신도의 모범으로 계속해서 몇 가지 예외를 살펴 보겠습니다. 친구에게 전화했는데 집에 없었다면 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) );

요청 시간과 오버 헤드를 측정하기위한 고급 방법이 있지만 이것이 일반적으로 내 요구에 충분합니다.

요청하지 않은 연결을 닫는 방법에 대한 자세한 내용은 언제 URL 연결이 닫히나요?를 참조하십시오 . .


답변

Tim Bray는 openConnection ()이 실제 연결을 설정하지 않는다고 간략하게 설명했습니다. 오히려 getInputStream () 또는 getOutputStream ()과 같은 메소드를 호출 할 때까지 실제 HTTP 연결이 설정되지 않습니다.

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


답변

HTTPURLConnection은 어느 시점에서 주어진 URL에 연결을 시도합니까?

URL에 이름이 지정된 포트 (있는 경우), 그렇지 않으면 HTTP의 경우 80, HTTPS의 경우 443 나는 이것이 문서화되었다고 믿는다.

어느 시점에서 연결을 성공적으로 설정할 수 있었는지 알 수 있습니까?

예외없이 getInputStream () 또는 getOutputStream () 또는 getResponseCode ()를 호출 할 때

한 단계 / 방법 호출로 연결을 설정하고 실제 요청을 전송합니까? 어떤 방법입니까?

아뇨.

일반인의 용어로 getOutputStream 및 getInputStream의 기능을 설명 할 수 있습니까?

필요한 경우 먼저 연결 한 다음 필요한 스트림을 반환합니다.

연결하려는 서버가 다운되면 getOutputStream에서 예외가 발생합니다. getOutputStream을 호출 할 때 HTTPURLConnection이 연결을 시작하기 시작한다는 것을 의미합니까? getInputStream은 어떻습니까? getInputStream에서만 응답을 얻을 수 있기 때문에 getOutputStream에서 요청을 보내지 않고 단순히 연결을 설정한다는 의미입니까? getInputStream을 호출 할 때 HttpURLConnection이 서버로 돌아가서 응답을 요청합니까?

위 참조.

openConnection이 단순히 새로운 연결 객체를 생성하지만 아직 연결을 설정하지 않았다고 말하는 것이 맞습니까?

예.

읽기 오버 헤드 및 연결 오버 헤드를 어떻게 측정 할 수 있습니까?

연결 : getInoutStream () 또는 getOutputStream ()이 리턴하는 데 걸리는 시간을 사용하십시오 (둘 중 먼저 호출). 읽기 : 첫 번째 읽기 시작부터 EOS 가져 오기까지의 시간입니다.


답변

HTTPURLConnection은 어느 시점에서 주어진 URL에 연결을 시도합니까?

분명히 설명 할 가치가 있습니다 .’UrlConnection ‘인스턴스가 있고 기본 Tcp / Ip / SSL 소켓 연결이 있습니다. ‘UrlConnection’또는 ‘HttpUrlConnection’인스턴스는 단일 HTTP 페이지 요청과 동의어이며 url.openConnection ()을 호출 할 때 작성됩니다. 그러나 하나의 ‘url’인스턴스에서 여러 개의 url.openConnection ()을 수행하면 운이 좋으면 동일한 Tcp / Ip 소켓과 SSL 핸드 쉐이킹을 재사용 할 것입니다 … 소켓을 설정하는 오버 헤드가 매우 높은 SSL을 사용하는 경우 동일한 서버에 많은 페이지 요청을 수행하는 것이 좋습니다.

참조 :
HttpURLConnection 구현


답변

저수준 패킷 교환을 캡처하기 위해 연습을했으며 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();


답변