2011-01-22 2 views
35

HttpURLConnection을 사용할 때 'get'을 사용하지 않으면 InputStream을 닫아야합니까?HttpURLConnection의 안전한 사용

즉 안전합니까?

HttpURLConnection conn = (HttpURLConnection) uri.getURI().toURL().openConnection(); 
conn.connect(); 
// check for content type I don't care about 
if (conn.getContentType.equals("image/gif") return; 
// get stream and read from it 
InputStream is = conn.getInputStream(); 
try { 
    // read from is 
} finally { 
    is.close(); 
} 

둘째, 그것의 모든 콘텐츠 완벽하고 읽기 것 전에의 InputStream 를 닫 안전합니까?

기본 소켓을 ESTABLISHED 또는 CLOSE_WAIT 상태로 둘 위험이 있습니까?

답변

25

이 안전하게 그것의 모든 전에의 InputStream 를 닫있어 콘텐츠 당신은 당신이 닫기 전에 입력 스트림에서 모든 데이터를 읽을 필요가

을 읽을 수있다 기본 TCP 연결이 캐시되도록 최신 Java에서는 필요하지 않아야한다고 읽었지만 연결 재사용에 대한 전체 응답을 읽는 것이 항상 의무화되었습니다.

확인이 게시물 : 당신이 정말 당신이 conn.disconnect()를 호출해야 연결이 가까이 있는지 확인하려면 keep-alive in java6

+0

매우 흥미 롭습니다. 이 질문은 실제로 내가 동일한 IP에 CLOSE_WAIT 소켓을 많이보고있는 문제에 대한 배경이지만 캐싱 (URLConnection.disconnect()를 명시 적으로 호출하지 않기 때문에) 중 하나 일 것으로 예상됩니다. 재사용해야합니다. – Joel

+3

@Joel : HttpUrlConnection.disconnect()를 호출하면 기본 TCP 소켓이 닫힙니다. 입력 스트림을 닫음으로써 기본 TCP 소켓은 나중에 재사용 할 수 있도록 풀링됩니다.유일한주의 사항은 TCP 연결이 캐시되도록 입력 스트림에서 전체 응답 (또는 전체 오류 응답)을 읽어야한다는 것입니다. 스트림에서 전체 데이터가 실제로 필요 하든지 상관없이 항상 조언을 제공합니다. 내 대답의 게시물을 확인하십시오 – Cratylus

+0

첫 번째 예제에 따라 모든 데이터를 읽지 않으면 어떻게됩니까? 여전히 IS를 닫아야한다고 생각하지만 데이터를 읽지는 않지만 여전히 캐시 된 상태로 유지됩니다 닫은. – Joel

1

HttpURLConnection을 사용할 때 'get'을 사용하지 않으면 InputStream을 닫아야합니까?

네, 항상 닫아야합니다.

즉 안전합니까?

NPE를받을 위험이 있습니다. 안전한입니다

InputStream is = null; 
try { 
    is = conn.getInputStream() 
    // read from is 
} finally { 
    if (is != null) { 
     is.close(); 
    } 
} 
+1

두 번째 질문은 근본적인 소켓 상태를 참조하는 것이므로 전체 런타임 코드 안전성과 관련하여 불완전한 스 니펫을 의도적으로 게시했습니다. 모든 내용이 읽히기 전에 소켓을 닫아서 CLOSE_WAIT 또는 ESTABLISED에 소켓의 위험이 있는지 알고 싶습니다. – Joel

+0

또는 IOUtils.closeQuietly (is)입니다. – Kirby

6

.

관찰 된 열린 연결은 HTTP 1.1 연결 유지 활성 기능 (HTTP Persistent Connections이라고도 함) 때문입니다. 서버가 HTTP 1.1을 지원하고 응답 헤더에 Connection: close을 보내지 않으면 Java는 입력 스트림을 닫을 때 언더 레이 TCP 연결을 즉시 닫지 않습니다. 대신이 서버는 열어 두어 동일한 서버에 대한 다음 HTTP 요청을 위해 다시 사용하려고 시도합니다.

전혀이 동작을하지 않으려면 false로 시스템 프로퍼티 http.keepAlive을 설정할 수 있습니다

System.setProperty("http.keepAlive","false"); 
+1

감사합니다. 연결이 사용 중이 아니라고 가정 할 때 닫히기 전에 캐시되는 시간을 알고 있으며이 시간 초과 기간을 제어 할 수있는 방법이 있습니까? – Joel

14

여기에 연결 유지 캐시에 관한 몇 가지 정보입니다. 이 모든 정보는 Java 6과 관련되어 있지만 이전 버전과 이후 버전에서는 정확할 수도 있습니다.

내가 무엇을 말할 수에서

코드는 아래로 비등 :

  1. 원격 서버가 양의 정수로 구문 분석 할 수있는 "타임 아웃"값으로 "연결 유지"헤더를 전송하는 경우, 그 시간 초과에 초 수가 사용됩니다.
  2. 원격 서버가 "Keep-Alive"헤더를 보내지 만 양수로 해석 할 수있는 "timeout"값이없는 경우 "usingProxy"가 true이면 시간 초과는 60 초입니다.
  3. 다른 모든 경우 시간 제한은 5 초입니다.

이 논리는 두 위치 사이에서 분할된다 sun.net.www.http.HttpClient 라인 (725) 주변 (이하 "parseHTTPHeader"방법), 및 sun.net.www.http.KeepAliveCache 라인 (120) 주변 (이하 "넣어"방법으로). 적절한 시간 제한 필드

  • JDK를 수정으로 연결 유지 헤더를 보낼 수

    1. 제어 원격 서버를 구성합니다


      그래서, 시간 초과 기간을 제어하는 ​​두 가지 방법이 있습니다 소스 코드를 직접 작성하십시오.

    내부 JDK 클래스를 다시 컴파일하지 않고 외관상으로 임의의 5 초 기본값을 변경할 수 있다고 생각하지만 그렇지 않습니다. 이 기능을 요청하는 2005 년에 bug이 제출되었지만 Sun은이를 제공하기를 거부했습니다.

  • +3

    잘 설명되어 있지 않은 주제에 대한 좋은 조사. 공유해 주셔서 감사합니다. –

    31

    http://docs.oracle.com/javase/6/docs/technotes/guides/net/http-keepalive.html 및 OpenJDK 소스 코드에 따르면.

    (킵 얼라이브 사실 ==)

    클라이언트가 HttpURLConnection.getInputSteam라고하면(). 닫기(), 나중에 HttpURLConnection을 호출합니다. 연결 해제() 소켓을 닫지 마십시오 소켓. 즉, 소켓이 재사용 (캐시 됨)

    클라이언트가 close()를 호출하지 않으면 disconnect()를 호출하면 InputSteam이 닫히고 Socket이 닫힙니다.

    그래서 소켓을 다시 사용하려면 을 닫고()을 호출하십시오. HttpURLConnection 을 호출하지 마십시오.().

    2
    HTTP 요청은 (200 아무것도하지만) 실패하는 경우 또한 오류 스트림을 닫을 필요

    :

    try { 
        ... 
    } 
    catch (IOException e) { 
        connection.getErrorStream().close(); 
    } 
    

    당신이 그것을 할 수없는 경우, 200을 반환하지 않는 모든 요청 (예 : 타임 아웃이됩니다) 한 소켓 누출. 자세한 내용은 http://scotte.github.io/2015/01/httpurlconnection-socket-leak/을 참조하십시오.

    +1

    그 마지막 소스 코드 (JDK 8u74)는'public InputStream getErrorStream() { return null; }' – FelixJongleur42

    관련 문제