Tại sao không thể, nếu không thử I / O, phát hiện ra rằng TCP socket đã được đóng bởi peer một cách duyên dáng?


92

Theo một câu hỏi gần đây , tôi tự hỏi tại sao trong Java không thể đọc / ghi trên ổ cắm TCP, để phát hiện ra rằng ổ cắm đã được đóng một cách duyên dáng bởi ngang hàng? Điều này dường như luôn xảy ra bất kể người ta sử dụng pre-NIO Sockethay NIO SocketChannel.

Khi một máy ngang hàng đóng kết nối TCP một cách duyên dáng, các ngăn xếp TCP ở cả hai phía của kết nối biết về sự thật. Phía máy chủ (phía bắt đầu tắt máy) kết thúc ở trạng thái FIN_WAIT2, trong khi phía máy khách (phía không phản hồi rõ ràng với việc tắt máy) kết thúc ở trạng thái CLOSE_WAIT. Tại sao không có phương thức nào trong Sockethoặc SocketChannelcó thể truy vấn ngăn xếp TCP để xem liệu kết nối TCP bên dưới đã bị chấm dứt hay chưa? Có phải là ngăn xếp TCP không cung cấp thông tin trạng thái như vậy không? Hay đó là một quyết định thiết kế để tránh một cuộc gọi tốn kém vào hạt nhân?

Với sự trợ giúp của những người dùng đã đăng một số câu trả lời cho câu hỏi này, tôi nghĩ rằng tôi biết vấn đề có thể đến từ đâu. Bên không đóng kết nối một cách rõ ràng sẽ ở trạng thái TCP CLOSE_WAITcó nghĩa là kết nối đang trong quá trình tắt và chờ bên đưa ra CLOSEhoạt động riêng của mình . Tôi cho rằng nó đủ công bằng để isConnectedtrả lại trueisClosedtrả lại false, nhưng tại sao không có cái gì đó như thế isClosing?

Dưới đây là các lớp thử nghiệm sử dụng ổ cắm trước NIO. Nhưng kết quả giống hệt nhau thu được bằng cách sử dụng NIO.

import java.net.ServerSocket;
import java.net.Socket;

public class MyServer {
  public static void main(String[] args) throws Exception {
    final ServerSocket ss = new ServerSocket(12345);
    final Socket cs = ss.accept();
    System.out.println("Accepted connection");
    Thread.sleep(5000);
    cs.close();
    System.out.println("Closed connection");
    ss.close();
    Thread.sleep(100000);
  }
}


import java.net.Socket;

public class MyClient {
  public static void main(String[] args) throws Exception {
    final Socket s = new Socket("localhost", 12345);
    for (int i = 0; i < 10; i++) {
      System.out.println("connected: " + s.isConnected() + 
        ", closed: " + s.isClosed());
      Thread.sleep(1000);
    }
    Thread.sleep(100000);
  }
}

Khi máy khách thử nghiệm kết nối với máy chủ thử nghiệm, đầu ra vẫn không thay đổi ngay cả sau khi máy chủ bắt đầu tắt kết nối:

connected: true, closed: false
connected: true, closed: false
...

Tôi nên đề cập đến: Giao thức SCTP không có "vấn đề" này. SCTP không có trạng thái nửa đóng như TCP, nói cách khác là một bên không thể tiếp tục gửi dữ liệu trong khi đầu kia đã đóng ổ cắm gửi của nó. Điều đó sẽ làm cho mọi thứ dễ dàng hơn.
Tarnay Kálmán

2
Chúng tôi có hai Hộp thư (Sockets) ........................................... ...... Các Hộp thư gửi thư tới eachother bằng RoyalMail (IP) quên TCP ............................. .................... Tất cả đều ổn và tuyệt vời, các hộp thư có thể gửi / nhận thư đến các đơn vị khác (gần đây có nhiều độ trễ) gửi trong khi nhận không có vấn đề gì. ............. Nếu một Hộp thư bị một chiếc xe tải đâm và hỏng .... thì Hộp thư kia sẽ biết như thế nào? Nó sẽ phải được notifyed bởi Royal Mail, những người lần lượt wouldnt biết cho đến khi nó bên cạnh tryed gửi / recieving mail từ đó thất bại Mailbox .. ...... erm .....
divinci

Nếu bạn không đọc từ ổ cắm hoặc ghi vào ổ cắm, tại sao bạn lại quan tâm? Và nếu bạn định đọc từ ổ cắm hoặc ghi vào ổ cắm, tại sao phải kiểm tra thêm? Trường hợp sử dụng là gì?
David Schwartz

2
Socket.closekhông phải là một sự gần gũi duyên dáng.
user253751

@immibis Đây chắc chắn là một kết thúc duyên dáng, trừ khi có dữ liệu chưa đọc trong bộ đệm nhận ổ cắm hoặc bạn đã nhầm với SO_LINGER.
Marquis of Lorne,

Câu trả lời:


29

Tôi đã sử dụng Socket thường xuyên, chủ yếu là với Bộ chọn và mặc dù không phải là chuyên gia về OSI Mạng, theo hiểu biết của tôi, việc gọi shutdownOutput()trên Socket thực sự gửi một thứ gì đó trên mạng (FIN) đánh thức Bộ chọn của tôi ở phía bên kia (hành vi tương tự trong C ngôn ngữ). Ở đây bạn có khả năng phát hiện : thực sự phát hiện một thao tác đọc sẽ không thành công khi bạn thử nó.

Trong đoạn mã bạn cung cấp, việc đóng ổ cắm sẽ tắt cả hai luồng đầu vào và đầu ra, không có khả năng đọc dữ liệu có thể có sẵn, do đó sẽ mất chúng. Socket.close()Phương thức Java thực hiện ngắt kết nối "duyên dáng" (ngược lại với những gì tôi nghĩ ban đầu) trong đó dữ liệu còn lại trong luồng đầu ra sẽ được gửi theo sau bởi một FIN để báo hiệu kết thúc của nó. FIN sẽ là ACK ở phía bên kia, như bất kỳ gói thông thường nào sẽ là 1 .

Nếu bạn cần đợi mặt bên kia đóng ổ cắm, bạn cần đợi FIN của nó. Và để đạt được điều đó, bạn phải phát hiện Socket.getInputStream().read() < 0, có nghĩa là bạn không nên đóng ổ cắm của mình, vì nó sẽ đóngInputStream ổ cắm .

Từ những gì tôi đã làm trong C và bây giờ trong Java, việc đạt được mức đóng đồng bộ như vậy nên được thực hiện như sau:

  1. Đầu ra của ổ cắm tắt máy (gửi FIN ở đầu bên kia, đây là thứ cuối cùng sẽ được gửi bởi ổ cắm này). Đầu vào vẫn đang mở để bạn có thể read()phát hiện và điều khiển từ xaclose()
  2. Đọc socket InputStreamcho đến khi chúng tôi nhận được reply-FIN từ đầu bên kia (vì nó sẽ phát hiện ra FIN, nó sẽ trải qua cùng một quá trình kết nối duyên dáng). Điều này quan trọng đối với một số hệ điều hành vì chúng không thực sự đóng socket miễn là một trong các bộ đệm của nó vẫn chứa dữ liệu. Chúng được gọi là ổ cắm "ma" và sử dụng hết số bộ mô tả trong HĐH (đó có thể không còn là vấn đề với HĐH hiện đại)
  3. Đóng ổ cắm (bằng cách gọi Socket.close()hoặc đóng InputStreamhoặc OutputStream)

Như được hiển thị trong đoạn mã Java sau:

public void synchronizedClose(Socket sok) {
    InputStream is = sok.getInputStream();
    sok.shutdownOutput(); // Sends the 'FIN' on the network
    while (is.read() > 0) ; // "read()" returns '-1' when the 'FIN' is reached
    sok.close(); // or is.close(); Now we can close the Socket
}

Tất nhiên cả hai bên phải sử dụng cùng một cách đóng, hoặc phần gửi có thể luôn gửi đủ dữ liệu để giữ cho whilevòng lặp bận rộn (ví dụ: nếu phần gửi chỉ gửi dữ liệu và không bao giờ đọc để phát hiện kết thúc kết nối. Điều nào là vụng về, nhưng bạn có thể không kiểm soát được điều đó).

Như @WarrenDew đã chỉ ra trong nhận xét của mình, việc loại bỏ dữ liệu trong chương trình (lớp ứng dụng) gây ra sự ngắt kết nối không duyên dáng ở lớp ứng dụng: mặc dù tất cả dữ liệu đã được nhận ở lớp TCP ( whilevòng lặp), chúng sẽ bị loại bỏ.

1 : Từ " Mạng cơ bản trong Java ": xem hình. 3.3 tr.45 và toàn bộ §3.7, trang 43-48


Java thực hiện một kết thúc duyên dáng. Nó không phải là 'tàn bạo'.
Marquis of Lorne

2
@EJP, "ngắt kết nối duyên dáng" là một trao đổi cụ thể diễn ra ở cấp TCP, nơi Khách hàng sẽ báo hiệu ý muốn của mình để ngắt kết nối tới Máy chủ, đến lượt nó, sẽ gửi dữ liệu còn lại trước khi đóng bên của nó. Phần "gửi dữ liệu còn lại" phải được xử lý bởi chương trình (mặc dù mọi người sẽ không gửi bất cứ thứ gì trong hầu hết thời gian). Gọi socket.close()là "tàn bạo" theo cách nó không tôn trọng tín hiệu Client / Server này. Máy chủ sẽ được thông báo về việc ngắt kết nối Máy khách chỉ khi bộ đệm đầu ra ổ cắm của chính nó đầy (vì không có dữ liệu nào được truy cập bởi phía bên kia, bộ đệm đã bị đóng).
Matthieu

Xem MSDN để biết thêm thông tin.
Matthieu

Java không buộc các ứng dụng gọi Socket.close () thay vì 'gửi [ing] dữ liệu còn lại trước tiên và nó không ngăn chúng sử dụng chế độ tắt trước. Socket.close () chỉ thực hiện những gì đóng (sd). Về mặt này, Java không khác gì chính API Berkeley Sockets. Thật vậy, trong hầu hết các khía cạnh.
Marquis of Lorne,

2
@LeonidUsov Điều đó chỉ đơn giản là không chính xác. Java read()trả về -1 ở cuối luồng và tiếp tục làm như vậy, tuy nhiên nhiều lần bạn gọi nó. AC read()hoặc recv()trả về số 0 ở cuối luồng và tiếp tục làm như vậy tuy nhiên nhiều lần bạn gọi nó.
Marquis of Lorne,

18

Tôi nghĩ đây là một câu hỏi về lập trình ổ cắm nhiều hơn. Java chỉ theo truyền thống lập trình socket.

Từ Wikipedia :

TCP cung cấp sự phân phối có thứ tự, đáng tin cậy của một dòng byte từ một chương trình trên máy tính này sang chương trình khác trên máy tính khác.

Khi quá trình bắt tay được thực hiện, TCP không phân biệt hai điểm cuối (máy khách và máy chủ). Thuật ngữ "máy khách" và "máy chủ" hầu hết là để thuận tiện. Vì vậy, "máy chủ" có thể đang gửi dữ liệu và "máy khách" có thể gửi một số dữ liệu khác đồng thời cho nhau.

Thuật ngữ "Đóng" cũng gây hiểu nhầm. Chỉ có tuyên bố FIN, có nghĩa là "Tôi sẽ không gửi cho bạn bất kỳ thứ gì nữa." Nhưng điều này không có nghĩa là không có gói nào trong chuyến bay, hoặc gói kia không còn gì để nói. Nếu bạn triển khai thư ốc làm lớp liên kết dữ liệu hoặc nếu gói của bạn đi theo các tuyến đường khác nhau, có thể người nhận nhận các gói không đúng thứ tự. TCP biết cách sửa lỗi này cho bạn.

Ngoài ra, bạn, với tư cách là một chương trình, có thể không có thời gian để tiếp tục kiểm tra những gì trong bộ đệm. Vì vậy, khi thuận tiện, bạn có thể kiểm tra những gì trong bộ đệm. Nhìn chung, việc triển khai socket hiện tại không quá tệ. Nếu thực sự có isPeerClosed (), đó là cuộc gọi bổ sung mà bạn phải thực hiện mỗi khi bạn muốn gọi đọc.


1
Tôi không nghĩ vậy, bạn có thể kiểm tra các trạng thái trong mã C trên cả windows và linux !!! Vì một số lý do, Java có thể không hiển thị một số nội dung, giống như việc hiển thị các hàm getsockopt có trên windows và linux là một điều tuyệt vời. Trên thực tế, các câu trả lời dưới đây có một số mã C của linux cho phía linux.
Dean Hiller

Tôi không nghĩ rằng có phương thức 'isPeerClosed ()' bằng cách nào đó cam kết bạn gọi nó trước mỗi lần đọc. Bạn chỉ có thể gọi nó một cách đơn giản khi cần rõ ràng. Tôi đồng ý rằng việc triển khai ổ cắm hiện tại không quá tệ, mặc dù nó yêu cầu bạn ghi vào luồng đầu ra nếu bạn muốn tìm hiểu xem phần từ xa của ổ cắm có bị đóng hay không. Bởi vì nếu nó không phải là, bạn phải xử lý dữ liệu bằng văn bản của bạn ở phía bên kia là tốt, và nó chỉ đơn giản là như số tiền lớn của niềm vui như ngồi trên móng tay hướng theo chiều dọc;)
Jan Hruby

1
nghĩa là 'không còn gói nào nữa trong chuyến bay'. FIN được nhận sau bất kỳ dữ liệu nào trong chuyến bay. Tuy nhiên điều đó không có nghĩa là đồng đẳng đã đóng ổ cắm cho đầu vào. Bạn phải ** gửi nội dung nào đó * và 'thiết lập lại kết nối' để phát hiện điều đó. FIN có thể chỉ có nghĩa là tắt đầu ra.
Marquis of Lorne

11

API ổ cắm bên dưới không có thông báo như vậy.

Ngăn xếp TCP đang gửi sẽ không gửi bit FIN cho đến khi gói cuối cùng, vì vậy có thể có rất nhiều dữ liệu được đệm từ khi ứng dụng gửi đóng socket của nó một cách hợp lý trước khi dữ liệu đó được gửi đi. Tương tự như vậy, dữ liệu được lưu vào bộ đệm vì mạng nhanh hơn ứng dụng nhận (tôi không biết, có thể bạn đang chuyển tiếp nó qua kết nối chậm hơn) có thể quan trọng đối với người nhận và bạn sẽ không muốn ứng dụng nhận loại bỏ nó chỉ vì bit FIN đã được ngăn xếp nhận.


Trong ví dụ thử nghiệm của tôi (có lẽ tôi nên cung cấp một cái ở đây ...), không có dữ liệu nào được gửi / nhận qua kết nối có chủ đích. Vì vậy, tôi khá chắc chắn rằng ngăn xếp nhận được FIN (có duyên) hoặc RST (trong một số trường hợp không có duyên). Điều này cũng được xác nhận bởi netstat.
Alexander

1
Chắc chắn - nếu không có gì được lưu vào bộ đệm thì FIN sẽ được gửi ngay lập tức, trên một gói trống (không có trọng tải). Tuy nhiên, sau khi FIN, không còn gói dữ liệu nào được gửi bởi đầu kết nối đó (nó sẽ vẫn ACK bất cứ thứ gì được gửi đến nó).
Mike Dimmick 30/09/08

1
Điều gì xảy ra là các mặt của kết nối kết thúc bằng <code> CLOSE_WAIT </code> và <code> FIN_WAIT_2 </code> và ở trạng thái này là <code> isConcected () </code> và <code> isClosed () </code> vẫn không thấy rằng kết nối đã bị ngắt.
Alexander

Cảm ơn những đề xuất của bạn! Tôi nghĩ bây giờ tôi đã hiểu vấn đề tốt hơn. Tôi đã đặt câu hỏi cụ thể hơn (xem đoạn thứ ba): tại sao không có "Socket.isClosing" để kiểm tra các kết nối nửa kín?
Alexander

8

Vì không có câu trả lời nào cho đến nay trả lời đầy đủ cho câu hỏi, tôi đang tóm tắt hiểu biết hiện tại của mình về vấn đề này.

Khi một kết nối TCP được thiết lập và một cuộc gọi ngang hàng close()hoặc shutdownOutput()trên ổ cắm của nó, ổ cắm ở phía bên kia của kết nối sẽ chuyển sang CLOSE_WAITtrạng thái. Về nguyên tắc, có thể tìm ra từ ngăn xếp TCP xem một ổ cắm có ở CLOSE_WAITtrạng thái mà không cần gọi hay không read/recv(ví dụ: getsockopt()trên Linux: http://www.developerweb.net/forum/showthread.php?t=4395 ), nhưng không phải vậy. xách tay.

SocketLớp của Java dường như được thiết kế để cung cấp một sự trừu tượng có thể so sánh với một BSD TCP socket, có lẽ vì đây là mức trừu tượng mà mọi người quen dùng khi lập trình các ứng dụng TCP / IP. Các ổ cắm BSD là một ổ cắm hỗ trợ tổng quát khác với các ổ cắm chỉ INET (ví dụ: TCP), vì vậy chúng không cung cấp một cách di động để tìm ra trạng thái TCP của một ổ cắm.

Không có phương pháp nào giống như isCloseWait()vì mọi người thường lập trình các ứng dụng TCP ở mức độ trừu tượng được cung cấp bởi các socket BSD không mong đợi Java cung cấp bất kỳ phương thức bổ sung nào.


3
Java cũng không thể cung cấp bất kỳ phương thức bổ sung nào, có thể di động được. Có thể họ có thể tạo một phương thức isCloseWait () sẽ trả về false nếu nền tảng không hỗ trợ nó, nhưng có bao nhiêu lập trình viên sẽ bị cắn bởi gotcha đó nếu họ chỉ thử nghiệm trên các nền tảng được hỗ trợ?
ephemient

1
có vẻ như nó có thể di động với tôi ... cửa sổ có msdn.microsoft.com/en-us/library/windows/desktop/… và linux này pubs.opengroup.org/onlinepubs/009695399/functions/…
Dean Hiller

1
Nó không phải là các lập trình viên đã quen với nó; đó là giao diện socket là thứ hữu ích cho các lập trình viên. Hãy nhớ rằng trừu tượng hóa socket được sử dụng cho nhiều thứ hơn là giao thức TCP.
Warren Dew

Không có phương pháp nào trong Java giống như isCloseWait()vì nó không được hỗ trợ trên tất cả các nền tảng.
Marquis of Lorne

Giao thức nhận dạng (RFC 1413) cho phép máy chủ giữ kết nối mở sau khi gửi phản hồi hoặc đóng nó mà không cần gửi thêm dữ liệu. Một ứng dụng khách Java nhận dạng có thể chọn giữ kết nối mở để tránh bắt tay 3 bước trong lần tra cứu tiếp theo, nhưng làm thế nào nó biết kết nối vẫn đang mở? Nó chỉ cần thử, phản hồi bất kỳ lỗi nào bằng cách mở lại kết nối? Hay đó là lỗi thiết kế giao thức?
david

7

Có thể thực hiện việc phát hiện phía từ xa của kết nối socket (TCP) đã đóng hay chưa bằng phương thức java.net.Socket.sendUrgentData (int) và bắt IOException mà nó ném ra nếu phía từ xa bị hỏng. Điều này đã được thử nghiệm giữa Java-Java và Java-C.

Điều này tránh được vấn đề khi thiết kế giao thức truyền thông để sử dụng một số loại cơ chế ping. Bằng cách tắt OOBInline trên socket (setOOBInline (false), bất kỳ dữ liệu OOB nào nhận được sẽ bị loại bỏ âm thầm, nhưng dữ liệu OOB vẫn có thể được gửi. Nếu mặt điều khiển từ xa bị đóng, việc đặt lại kết nối được thử, không thành công và khiến một số IOException bị ném .

Nếu bạn thực sự sử dụng dữ liệu OOB trong giao thức của mình, thì số dặm của bạn có thể thay đổi.


4

ngăn xếp Java IO chắc chắn sẽ gửi FIN khi nó bị hủy theo một lần chia nhỏ đột ngột. Chỉ có nghĩa là bạn không thể phát hiện điều này, b / c hầu hết các máy khách chỉ gửi FIN nếu họ đang tắt kết nối.

... một lý do khác khiến tôi thực sự bắt đầu ghét các lớp Java NIO. Có vẻ như mọi thứ là một chút nửa mông.


1
ngoài ra, có vẻ như tôi chỉ nhận được và kết thúc luồng khi đọc (-1 trở lại) khi có FIN. Vì vậy, đây là cách duy nhất tôi có thể thấy để phát hiện tắt máy ở phía đọc.

3
Bạn có thể phát hiện ra nó. Bạn nhận được EOS khi đọc. Và Java không gửi FIN. TCP thực hiện điều đó. Java không thực thi TCP / IP, nó chỉ sử dụng triển khai nền tảng.
Marquis of Lorne,

4

Đó là một chủ đề thú vị. Tôi vừa tìm hiểu mã java để kiểm tra. Từ phát hiện của tôi, có hai vấn đề khác biệt: thứ nhất là bản thân TCP RFC, cho phép ổ cắm đóng từ xa truyền dữ liệu trong chế độ bán song công, vì vậy ổ cắm đóng từ xa vẫn mở một nửa. Theo RFC, RST không đóng kết nối, bạn cần gửi một lệnh ABORT rõ ràng; vì vậy Java cho phép gửi dữ liệu thông qua một nửa kín

(Có hai phương pháp để đọc trạng thái đóng ở cả hai điểm cuối.)

Vấn đề khác là việc triển khai nói rằng hành vi này là tùy chọn. Khi Java cố gắng trở nên di động, chúng đã triển khai tính năng chung tốt nhất. Tôi đoán việc duy trì một bản đồ của (Hệ điều hành, triển khai bán song công) sẽ là một vấn đề.


1
Tôi cho rằng bạn đang nói về RFC 793 ( faqs.org/rfcs/rfc793.html ) Phần 3.5 Đóng kết nối. Tôi không chắc nó giải thích sự cố, vì cả hai bên đều hoàn tất việc tắt kết nối có duyên và kết thúc ở trạng thái mà họ không nên gửi / nhận bất kỳ dữ liệu nào.
Alexander 30-08

Phụ thuộc. bạn thấy bao nhiêu FIN trên ổ cắm? Ngoài ra, có thể là một vấn đề nền tảng cụ thể: có lẽ cửa sổ trả lời mỗi FIN với FIN và kết nối được đóng cửa vào cả hai đầu, nhưng hệ điều hành khác có thể không hành xử theo cách này, và đây là nơi mà vấn đề phát sinh 2
Lorenzo Boccaccia

1
Không, tiếc là không phải vậy. isOutputShutdown và isInputShutdown là điều đầu tiên mà mọi người thử khi gặp phải "khám phá" này, nhưng cả hai phương thức đều trả về false. Tôi vừa thử nghiệm nó trên Windows XP và Linux 2.6. Các giá trị trở lại của tất cả 4 phương pháp vẫn như cũ ngay cả sau khi một nỗ lực đọc
Alexander

1
Đối với hồ sơ, đây không phải là bán song công. Bán song công có nghĩa là chỉ một bên có thể gửi tại một thời điểm; cả hai bên vẫn có thể gửi.
rbp

1
isInputShutdown và isOutputShutdown kiểm tra đầu cuối cục bộ của kết nối - chúng là các bài kiểm tra để tìm xem bạn đã gọi shutdownInput hay shutdownOutput trên Socket này. Họ không cho bạn biết bất cứ điều gì về đầu cuối từ xa của kết nối.
Mike Dimmick

3

Đây là một lỗ hổng của các lớp socket OO của Java (và tất cả các lớp khác mà tôi đã xem xét) - không có quyền truy cập vào lệnh gọi hệ thống được chọn.

Câu trả lời đúng trong C:

struct timeval tp;  
fd_set in;  
fd_set out;  
fd_set err;  

FD_ZERO (in);  
FD_ZERO (out);  
FD_ZERO (err);  

FD_SET(socket_handle, err);  

tp.tv_sec = 0; /* or however long you want to wait */  
tp.tv_usec = 0;  
select(socket_handle + 1, in, out, err, &tp);  

if (FD_ISSET(socket_handle, err) {  
   /* handle closed socket */  
}  

Bạn có thể làm điều tương tự với getsocketop(... SOL_SOCKET, SO_ERROR, ...).
David Schwartz,

1
Bộ mô tả tệp lỗi sẽ không chỉ ra kết nối đã đóng. Vui lòng đọc hướng dẫn chọn lọc: 'exceptionfds - Bộ này được xem cho "các điều kiện đặc biệt". Trong thực tế, chỉ có một điều kiện ngoại lệ như vậy là phổ biến: tính khả dụng của dữ liệu ngoài băng tần (OOB) để đọc từ ổ cắm TCP. ' FIN không phải là dữ liệu OOB.
Jan Wrobel

1
Bạn có thể sử dụng lớp 'Bộ chọn' để truy cập cuộc gọi hệ thống 'select ()'. Nó sử dụng NIO mặc dù.
Matthieu

1
Không có gì đặc biệt về một kết nối đã bị tắt bởi phía bên kia.
David Schwartz

1
Nhiều nền tảng, bao gồm cả Java, làm cung cấp quyền truy cập vào các lựa chọn () gọi hệ thống.
Marquis of Lorne

2

Đây là một cách giải quyết khập khiễng. Sử dụng SSL;) và SSL thực hiện một quá trình bắt tay đóng khi chia nhỏ để bạn được thông báo về việc ổ cắm đã được đóng (hầu hết các triển khai dường như thực hiện một quá trình bắt tay thích hợp).


2
Làm thế nào để một người nhận được "thông báo" về việc ổ cắm bị đóng khi sử dụng SSL trong java?
Dave B

2

Lý do cho hành vi này (không dành riêng cho Java) là thực tế là bạn không nhận được bất kỳ thông tin trạng thái nào từ ngăn xếp TCP. Rốt cuộc, một socket chỉ là một xử lý tệp khác và bạn không thể tìm ra liệu có dữ liệu thực tế để đọc từ nó mà không thực sự cố gắng hay không ( select(2)sẽ không giúp ích gì ở đó, nó chỉ báo hiệu rằng bạn có thể thử mà không bị chặn).

Để biết thêm thông tin, hãy xem Câu hỏi thường gặp về ổ cắm Unix .


2
Các ổ cắm REAL cơ bản (trên Mac OS X và Linux) dựa trên các ổ cắm BSD, nhưng RB quản lý để cung cấp cho bạn lỗi 102 khi kết nối bị ngắt ở đầu bên kia. Vì vậy, tôi đồng ý với người đăng ban đầu, điều này sẽ khả thi và thật khập khiễng khi Java (và Cacao) không cung cấp nó.
Joe Strout

1
@JoeStrout RB chỉ có thể làm điều đó khi bạn thực hiện một số I / O. Không có API nào cung cấp cho bạn trạng thái kết nối mà không cần thực hiện I / O. Giai đoạn = Stage. Đây không phải là sự thiếu hụt Java. Trên thực tế, đó là do thiếu 'âm quay số' trong TCP, đây là một tính năng thiết kế có chủ ý.
Marquis of Lorne,

select() cho bạn biết liệu có dữ liệu hoặc EOS có sẵn để đọc mà không bị chặn hay không. 'Các tín hiệu mà bạn có thể thử mà không bị chặn' là vô nghĩa. Nếu đang ở chế độ không chặn, bạn luôn có thể thử mà không bị chặn. select()được điều khiển bởi dữ liệu trong bộ đệm nhận ổ cắm hoặc một FIN đang chờ xử lý hoặc khoảng trống trong bộ đệm gửi ổ cắm.
Marquis of Lorne

@EJP getsockopt(SO_ERROR)thì sao? Trong thực tế, thậm chí getpeernamesẽ cho bạn biết nếu ổ cắm vẫn được kết nối.
David Schwartz,

0

Chỉ ghi yêu cầu rằng các gói được trao đổi cho phép xác định sự mất kết nối. Một công việc phổ biến xung quanh là sử dụng tùy chọn KEEP ALIVE.


Tôi nghĩ rằng một điểm cuối được phép bắt đầu quá trình tắt kết nối duyên dáng bằng cách gửi một gói có FIN được đặt mà không cần ghi bất kỳ tải trọng nào.
Alexander

@Alexander Tất nhiên là có, nhưng điều đó không liên quan đến câu trả lời này, đó là về việc phát hiện ít kết nối hơn.
Marquis of Lorne,

-3

Khi nói đến việc xử lý các socket Java nửa mở, người ta có thể muốn xem xét isInputShutdown ()isOutputShutdown () .


Không. Điều đó chỉ cho bạn biết bạn đã gọi gì chứ không phải bạn bè đã gọi.
Marquis of Lorne

Muốn chia sẻ nguồn của bạn cho tuyên bố đó?
JimmyB

3
Muốn chia sẻ nguồn của bạn cho người đối diện? Đó là tuyên bố của bạn. Nếu bạn có một bằng chứng, hãy có nó. Tôi khẳng định rằng bạn không chính xác. Làm thí nghiệm và chứng minh tôi sai.
Marquis of Lorne,

Ba năm sau và không có thử nghiệm. QED
Marquis of Lorne,
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.