2011-01-22 40 views
35

Khi sử dụng HttpURLConnection, InputStream có cần được đóng nếu chúng tôi không 'nhận' và sử dụng nó không?Sử dụng an toàn HttpURLConnection

tức là điều này có an toàn không?

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

Thứ hai, nó là an toàn để đóng một InputStream trước khi tất cả các nội dung của nó đã được hoàn toàn đọc?

Có nguy cơ rời khỏi ổ cắm cơ bản trong trạng thái ESTABLISHED hoặc thậm chí CLOSE_WAIT không?

Trả lời

25

là nó an toàn để đóng một InputStream trước khi tất cả các nội dung của nó đã được đọc

Bạn cần phải đọc tất cả các dữ liệu trong các dòng đầu vào trước khi bạn đóng nó để kết nối TCP cơ bản được lưu trữ. Tôi đã đọc rằng nó không nên được yêu cầu trong Java mới nhất, nhưng nó luôn luôn là bắt buộc để đọc toàn bộ phản ứng cho tái sử dụng kết nối.

Kiểm tra bài đăng này: keep-alive in java6

+0

Rất thú vị. Câu hỏi này thực sự là nền tảng cho một vấn đề tôi có, nơi tôi nhìn thấy LOTS của CLOSE_WAIT ổ cắm vào cùng một IP, nhưng vì bộ nhớ đệm (tôi không gọi URLConnection.disconnect() một cách rõ ràng) Tôi hy vọng có được chỉ có một, mà nên được sử dụng lại. – Joel

+3

@Joel: Bằng cách gọi 'HttpUrlConnection.disconnect()', socket tcp nằm bên dưới bị đóng. Bằng cách đóng luồng đầu vào, socket tcp bên dưới được gộp lại để sử dụng lại sau này.Thông báo trước duy nhất là toàn bộ phản hồi (hoặc toàn bộ phản hồi lỗi) phải được đọc từ luồng đầu vào để kết nối tcp được lưu trữ. Điều này luôn được đề cập bất kể bạn thực sự cần toàn bộ dữ liệu từ luồng. Kiểm tra bài đăng trong câu trả lời của tôi – Cratylus

+0

Điều gì xảy ra nếu bạn không đọc bất kỳ dữ liệu nào, như ví dụ đầu tiên của tôi - tôi đoán bạn vẫn cần đóng IS, nhưng nó vẫn không được lưu vào bộ nhớ cache nếu không có dữ liệu nào được đọc. đã đóng cửa. – Joel

1

Khi sử dụng HttpURLKết nối InputStream có cần phải đóng nếu chúng tôi không 'nhận' và sử dụng không?

Có, nó luôn luôn cần phải được đóng lại.

tức là điều này có an toàn không?

Không 100%, bạn có nguy cơ bị NPE. An toàn hơn là:

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

Câu hỏi thứ hai là tham chiếu đến trạng thái socket cơ bản, tôi đã cố tình đăng một đoạn mã không đầy đủ liên quan đến toàn bộ mã an toàn trong thời gian chạy. Tôi thực sự muốn biết nếu có một mối nguy hiểm của một ổ cắm còn lại trong CLOSE_WAIT hoặc ESTABLISED bằng cách đóng socket trước khi tất cả nội dung được đọc, – Joel

+0

Hoặc 'IOUtils.closeQuietly (is)' – Kirby

6

Nếu bạn thực sự muốn chắc chắn rằng kết nối gần bạn nên gọi conn.disconnect().

Các kết nối mở mà bạn quan sát được là do kết nối HTTP 1.1 duy trì tính năng còn sống (còn được gọi là HTTP Persistent Connections). Nếu máy chủ hỗ trợ HTTP 1.1 và không gửi Connection: close trong tiêu đề phản hồi Java không ngay lập tức đóng kết nối TCP underlaying khi bạn đóng luồng đầu vào. Thay vào đó nó giữ nó mở và cố gắng tái sử dụng nó cho yêu cầu HTTP tiếp theo đến cùng một máy chủ.

Nếu bạn không muốn hành vi này ở tất cả các bạn có thể thiết lập các hệ thống sở hữu http.keepAlive false:

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

Cảm ơn. Giả sử kết nối không được sử dụng, bạn có biết nó được lưu trong bao lâu trước khi bị đóng và có cách nào để kiểm soát thời gian chờ này không? – Joel

14

Dưới đây là một số thông tin liên quan đến bộ nhớ cache Keep-Alive. Tất cả thông tin này liên quan đến Java 6, nhưng có lẽ cũng chính xác cho nhiều phiên bản trước và sau.

Từ những gì tôi có thể nói, mã boils xuống:

  1. Nếu máy chủ từ xa gửi một tiêu đề "Keep-Alive" với một giá trị "timeout" có thể được phân tích như một số nguyên dương, mà số giây được sử dụng cho thời gian chờ.
  2. Nếu máy chủ từ xa gửi tiêu đề "Giữ bí mật" nhưng máy chủ không có giá trị "hết giờ" có thể được phân tích cú pháp thành số nguyên dương "usingProxy", thì thời gian chờ là 60 giây.
  3. Trong tất cả các trường hợp khác, thời gian chờ là 5 giây.

Logic này được chia giữa hai địa điểm: xung quanh dòng 725 của sun.net.www.http.HttpClient (trong phương pháp "parseHTTPHeader"), và xung quanh dòng 120 của sun.net.www.http.KeepAliveCache (trong phương pháp "đặt").


Vì vậy, có hai cách để kiểm soát thời gian timeout:

  1. kiểm soát máy chủ từ xa và cấu hình nó để gửi một tiêu đề Giữ-Alive với lĩnh vực thời gian chờ đúng
  2. Sửa JDK mã nguồn và xây dựng mã nguồn của riêng bạn.

Người ta sẽ nghĩ rằng có thể thay đổi tùy chọn mặc định năm giây tùy ý mà không cần biên dịch lại các lớp JDK nội bộ, nhưng không phải. A bug đã được nộp vào năm 2005 yêu cầu khả năng này, nhưng Sun từ chối cung cấp nó.

+3

Nghiên cứu hay về một chủ đề kém tài liệu. Cảm ơn bạn đã chia sẻ. –

31

Theo số http://docs.oracle.com/javase/6/docs/technotes/guides/net/http-keepalive.html và mã nguồn OpenJDK.

(Khi keepAlive == true)

Nếu khách hàng gọi HttpURLConnection.getInputSteam(). đóng(), lệnh gọi sau đến HttpURLConnection. ngắt kết nối() sẽ KHÔNG đóng Ổ cắm. tức là Ổ cắm được tái sử dụng (được lưu trong bộ nhớ cache)

Nếu khách hàng không gọi close(), ngắt kết nối cuộc gọi() sẽ đóng InputSteam và đóng Ổ cắm.

Vì vậy, để sử dụng lại Ổ cắm, chỉ cần gọi InputStream đóng(). Không gọi HttpURLConnection ngắt kết nối().

2

Bạn cũng phải đóng dòng lỗi nếu yêu cầu HTTP không (bất cứ điều gì nhưng 200):

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

Nếu bạn không làm điều đó, tất cả các yêu cầu mà không gửi lại 200 (ví dụ như thời gian chờ) sẽ rò rỉ một ổ cắm. Xem http://scotte.github.io/2015/01/httpurlconnection-socket-leak/ để biết thêm chi tiết.

+1

Không hoàn toàn chắc chắn về điều đó - mã nguồn cuối cùng (JDK 8u74) đọc 'public InputStream getErrorStream() { return null; } ' – FelixJongleur42

Các vấn đề liên quan