Đây là câu trả lời đầu tiên của tôi (tạm thời bị xóa), nhưng dường như không đủ tốt, kể từ khi nó đã được -2'ed khá nhanh chóng ... Tôi đoán tôi nên thêm nhiều lời giải thích. 01
Không thể đóng một nửa kết nối cho ổ cắm SSL/TLS để tuân thủ giao thức TLS, như được chỉ định trong section on closure alerts.
Máy khách và máy chủ phải chia sẻ kiến thức rằng kết nối là kết thúc để tránh bị cắt ngắn tấn công. Một trong hai bên có thể bắt đầu việc trao đổi tin nhắn đóng.
close_notify
Thông báo này thông báo cho người nhận rằng người gửi sẽ không gửi thêm bất kỳ tin nhắn nào trên kết nối này. Phiên sẽ trở thành không thể thay đổi nếu bất kỳ kết nối nào bị chấm dứt mà không có thông báo close_notify đúng với mức độ bằng cảnh báo.
Một trong hai bên có thể bắt đầu đóng bằng cách gửi cảnh báo close_notify. Bất kỳ dữ liệu nào nhận được sau khi đóng cảnh báo bị bỏ qua.
Mỗi bên được yêu cầu gửi cảnh báo close_notify trước khi đóng phần ghi của kết nối. Yêu cầu yêu cầu bên kia trả lời với cảnh báo close_notify riêng và đóng kết nối ngay lập tức, hủy bất kỳ đang chờ xử lý nào. Nó không được yêu cầu cho người khởi xướng việc đóng để chờ đợi đối với cảnh báo close_notify phản hồi trước khi đóng kết nối của số kết nối.
Mặc dù bạn có thể muốn đóng chỉ là một nửa của kết nối (đầu vào hay đầu ra thông qua shutdownInput()
/shutdownOutput()
), lớp TLS cơ bản vẫn cần phải gửi close_notify
gói này trước khi thực sự tắt thông tin liên lạc, nếu không, nó sẽ được được coi là một cuộc tấn công cắt ngắn.
Khắc phục khá đơn giản: chỉ sử dụng close()
trên SSLSocket
s: nó không chỉ đóng kết nối như đối với các cổng TCP bình thường, mà nó còn sạch theo đặc tả SSL/TLS.
EDIT: Giải thích bổ sung.
Câu trả lời ngắn gọn vẫn là: sử dụng close()
, bạn cũng có thể cần tìm hiểu thời điểm thích hợp để thực hiện điều đó từ giao thức ở trên SSL/TLS.
(Chỉ cần làm rõ, những gì bạn đang cố gắng thực hiện có hiệu quả là proxy "Man-In-The-Middle" (MITM). Bạn sẽ cần máy khách được cấu hình để tin cậy chứng chỉ của proxy, có thể như thể đó là chứng chỉ máy chủ nếu điều này có nghĩa là để xảy ra một cách minh bạch. Làm thế nào kết nối HTTPS làm việc thông qua một proxy HTTP là một different question.)
trong một số ý kiến của bạn để other related question của bạn, tôi có ấn tượng rằng bạn nghĩ rằng không trở -1
đã "phá vỡ giao thức TLS". Điều này không thực sự về giao thức, nhưng về API: cách các cấu trúc lập trình (các lớp, phương thức, hàm, ...) được cung cấp cho người dùng (lập trình viên) của ngăn xếp TLS để có thể sử dụng TLS. SSLSocket
chỉ là một phần của API để cung cấp cho các lập trình viên khả năng sử dụng SSL/TLS theo cách tương tự như đồng bằng Socket
. Đặc tả TLS không nói về ổ cắm. Trong khi TLS (và người tiền nhiệm của nó, SSL) được xây dựng với mục đích làm cho nó có thể cung cấp loại trừu tượng này, vào cuối ngày SSLSocket
chỉ là: trừu tượng hóa. Phù hợp với nguyên tắc thiết kế OOP, SSLSocket
kế thừa Socket
và cố gắng bám sát nhất có thể với hành vi của Socket
. Tuy nhiên, do cách SSL/TLS hoạt động (và vì một SSLSocket
có hiệu quả nằm trên đầu trang của một thông thường Socket
), không thể có bản đồ chính xác của tất cả các tính năng.
Cụ thể, khu vực chuyển đổi từ ổ cắm TCP thông thường sang ổ cắm SSL/TLS không thể được mô hình hóa một cách minh bạch. Một số lựa chọn tùy ý (ví dụ như cách bắt tay) phải được thực hiện khi thiết kế lớp SSLSocket
: đó là sự thỏa hiệp giữa không (a) phơi bày quá nhiều đặc trưng của TLS cho người dùng SSLSocket
, (b) tạo TLS công việc cơ chế và (c) ánh xạ tất cả điều này đến giao diện hiện có được cung cấp bởi lớp cha (Socket
). Những lựa chọn này chắc chắn có một số tác động đến chức năng của SSLSocket
và đó là lý do tại sao số Javadoc API page của nó có số lượng văn bản tương đối lớn. SSLSocket.close()
, về API, phải tuân theo mô tả của Socket.close()
. InputStream
/OutputStream
thu được từ số SSLSocket
không giống với số từ đồng bằng cơ bản Socket
, trong đó (trừ khi you've converted it explicitly) bạn có thể không nhìn thấy.
SSLSocket
được thiết kế để sử dụng được càng sát càng có thể như thể nó là một đồng bằng Socket
và InputStream
bạn nhận được thiết kế để hoạt động như chặt chẽ như có thể để một input stream tập tin dựa trên. Điều này không cụ thể đối với Java, nhân tiện. Ngay cả các ổ cắm Unix trong C cũng được sử dụng với một bộ mô tả tập tin và read()
. Những tóm tắt này thuận tiện và hoạt động phần lớn thời gian, miễn là bạn biết giới hạn của chúng là gì.
Khi nói đến read()
, ngay cả trong C, khái niệm về EOF
xuất phát từ việc chấm dứt kết thúc tệp nhưng bạn không thực sự xử lý tệp. Vấn đề với cổng TCP là, trong khi bạn có thể phát hiện khi bên từ xa đã chọn đóng kết nối khi nó gửi FIN
, bạn không thể phát hiện ra nó không có, nếu nó không gửi bất cứ điều gì. Khi đọc không có gì từ ổ cắm, không có cách nào để biết sự khác biệt giữa kết nối không hoạt động và bị hỏng. Như vậy, khi nói đến lập trình mạng, dựa vào việc đọc -1
từ InputStream
nếu được, nhưng bạn không bao giờ chỉ dựa vào điều đó, nếu không bạn có thể kết thúc với các kết nối chưa được phát hành. Trong khi "lịch sự" để một bên đóng kết nối TCP một cách chính xác (theo cách như bên từ xa đọc số -1
trên số InputStream
hoặc thứ gì đó tương đương, như được mô tả trong Orderly Versus Abortive Connection Release in Java), việc xử lý trường hợp này là không đủ. Đây là lý do tại sao các giao thức trên đầu trang của TCP thường là designed to give an indication as to when they're done sending data: ví dụ: SMTP gửi QUIT
(và có các thuật ngữ cụ thể), HTTP 1.1 sử dụng mã hóa Content-Length
hoặc chunked.
(Nhân tiện, nếu bạn không hài lòng với sự trừu tượng được cung cấp bởi SSLSocket
, hãy thử sử dụng trực tiếp SSLEngine
. Có thể khó hơn và sẽ không thay đổi thông số kỹ thuật TLS nói về việc gửi close_notify
.)
Nói chung với TCP, bạn nên biết, ở cấp giao thức ứng dụng, khi dừng gửi và nhận dữ liệu (để tránh các kết nối chưa được phát hành và/hoặc dựa vào các lỗi thời gian chờ). Câu này trong đặc tả TLS làm cho thực hành tốt này trở thành yêu cầu mạnh hơn khi nói đến TLS: "Yêu cầu bên kia phản hồi cảnh báo close_notify và đóng kết nối ngay lập tức loại bỏ mọi ghi đang chờ xử lý. " Bạn sẽ phải đồng bộ hóa bằng cách nào đó, thông qua giao thức ứng dụng hoặc bên từ xa vẫn có thể có thứ gì đó để viết (đặc biệt nếu bạn cho phép yêu cầu/phản hồi pipelined).
Nếu proxy bạn đang viết là để chặn kết nối HTTPS (dưới dạng MITM), bạn có thể xem nội dung của yêu cầu. Ngay cả khi các yêu cầu HTTP được pipelined, bạn có thể đếm số lượng phản hồi được gửi bởi máy chủ mục tiêu (và mong đợi khi chúng kết thúc, thông qua Content-Length hoặc phân tách đoạn).
Tuy nhiên, bạn sẽ không thể có thiết bị chặn "TLS" thuần túy trong suốt. TLS được thiết kế để đảm bảo việc vận chuyển giữa hai bên và không được đặt ở giữa. Trong khi hầu hết cơ chế để đạt được sự bảo vệ đó (tránh một MITM) được thực hiện thông qua chứng chỉ máy chủ, một số cơ chế TLS khác sẽ cản trở (cảnh báo đóng cửa là một trong số chúng). Hãy nhớ rằng bạn đang tạo hai kết nối TLS riêng biệt một cách hiệu quả, thực tế là chúng khó kết hợp như một kết quả không đáng ngạc nhiên, xem xét mục đích của TLS.
SSLSocket.close()
là cách phù hợp để đóng số SSLSocket
, nhưng giao thức ứng dụng cũng sẽ giúp bạn xác định thời điểm thích hợp để thực hiện việc này.
Ý của bạn là gì khi bạn nói shutdownInput/Output không được hỗ trợ? Liệu phương pháp ném một ngoại lệ? Nếu vậy, ngoại lệ là gì? – Pace
Có, nó thực sự ném một ngoại lệ 'Ngoại lệ trong chủ đề" Thread-4 "java.lang.UnsupportedOperationException: Phương thức shutdownInput() không được hỗ trợ trong SSLSocket'. Tôi cho rằng người hiểu biết đủ để trả lời câu hỏi của tôi, sẽ biết một thực tế là họ không được hỗ trợ với SSL. –
Bạn có thể làm rõ tại sao "bây giờ luồng thứ hai sẽ nhận -1 khi đọc" tại 'sockout.shutdownOutput()'? Có lẽ, trong ví dụ của bạn, 'in = sockin.getInputStream()'. – Bruno