Warum kann ohne E / A-Versuch nicht festgestellt werden, dass der TCP-Socket von einem Peer ordnungsgemäß geschlossen wurde?

91

Als Antwort auf eine aktuelle Frage frage ich mich, warum es in Java unmöglich ist, ohne den Versuch, auf einem TCP-Socket zu lesen / schreiben, festzustellen, dass der Socket vom Peer ordnungsgemäß geschlossen wurde. Dies scheint unabhängig davon der Fall zu sein, ob man das Pre-NIO Socketoder das NIO verwendet SocketChannel.

Wenn ein Peer eine TCP-Verbindung ordnungsgemäß schließt, wissen die TCP-Stapel auf beiden Seiten der Verbindung Bescheid. Die Serverseite (die das Herunterfahren initiiert) befindet sich im Status FIN_WAIT2, während die Clientseite (die nicht explizit auf das Herunterfahren reagiert) im Status endet CLOSE_WAIT. Warum gibt es nicht ein Verfahren in Socketoder SocketChanneldass der TCP - Stack abfragen können , um zu sehen , ob die zugrunde liegende TCP - Verbindung beendet wurde? Ist es so, dass der TCP-Stack solche Statusinformationen nicht bereitstellt? Oder ist es eine Entwurfsentscheidung, einen kostspieligen Aufruf des Kernels zu vermeiden?

Mit Hilfe der Benutzer, die bereits einige Antworten auf diese Frage gepostet haben, sehe ich, woher das Problem kommen könnte. Die Seite, die die Verbindung nicht explizit schließt, befindet sich im TCP-Status. Dies CLOSE_WAITbedeutet, dass die Verbindung gerade heruntergefahren wird und darauf wartet, dass die Seite ihren eigenen CLOSEVorgang ausführt. Ich , dass es fair genug nehme an, dass isConnectedErträge trueund isClosedRenditen false, aber warum nicht , wie es etwas gibt isClosing?

Nachfolgend sind die Testklassen aufgeführt, die Pre-NIO-Sockets verwenden. Mit NIO werden jedoch identische Ergebnisse erzielt.

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

Wenn der Testclient eine Verbindung zum Testserver herstellt, bleibt die Ausgabe unverändert, auch nachdem der Server das Herunterfahren der Verbindung initiiert hat:

connected: true, closed: false
connected: true, closed: false
...
Alexander
quelle
Ich dachte, ich würde erwähnen: Das SCTP-Protokoll hat dieses "Problem" nicht. SCTP hat keinen halb geschlossenen Zustand als TCP, mit anderen Worten, eine Seite kann keine Daten weiter senden, während das andere Ende seinen Sende-Socket geschlossen hat. Das sollte die Sache einfacher machen.
Tarnay Kálmán
2
Wir haben zwei Postfächer (Sockets) ........................................... ...... Die Postfächer senden sich über die RoyalMail (IP) gegenseitig E-Mails. Vergessen Sie TCP ............................. .................... Alles ist in Ordnung und gut, die Postfächer können E-Mails aneinander senden / empfangen (viel Verzögerung in letzter Zeit), ohne Probleme zu empfangen. ............. Wenn eine Mailbox von einem LKW angefahren würde und ausfallen würde ... wie würde die andere Mailbox das wissen? Es müsste von Royal Mail benachrichtigt werden, die ihrerseits bis zum nächsten Versuch, E-Mails von dieser fehlgeschlagenen Mailbox zu senden / zu empfangen, nicht wissen würde. ...... ähm .....
divinci
Warum interessiert es Sie, wenn Sie nicht aus dem Socket lesen oder in den Socket schreiben? Und wenn Sie aus dem Socket lesen oder in den Socket schreiben möchten, warum eine zusätzliche Überprüfung durchführen? Was ist der Anwendungsfall?
David Schwartz
2
Socket.closeist kein anmutiger Abschluss.
user253751
@immibis Es ist mit Sicherheit ein würdevoller Abschluss, es sei denn, der Socket-Empfangspuffer enthält ungelesene Daten oder Sie haben mit SO_LINGER herumgespielt.
Marquis von Lorne

Antworten:

29

Ich habe Sockets häufig verwendet, hauptsächlich mit Selektoren, und obwohl ich kein Network OSI-Experte bin, shutdownOutput()sendet der Aufruf eines Sockets nach meinem Verständnis tatsächlich etwas im Netzwerk (FIN), das meinen Selector auf der anderen Seite aufweckt (dasselbe Verhalten in C. Sprache). Hier haben Sie Erkennung : Sie erkennen tatsächlich einen Lesevorgang, der fehlschlägt, wenn Sie es versuchen.

In dem von Ihnen angegebenen Code werden durch Schließen des Sockets sowohl Eingabe- als auch Ausgabestreams heruntergefahren, ohne dass die verfügbaren Daten gelesen werden können, wodurch sie verloren gehen. Die Java- Socket.close()Methode führt eine "anmutige" Trennung durch (entgegengesetzt zu dem, was ich ursprünglich gedacht habe), indem die im Ausgabestream verbleibenden Daten gesendet werden, gefolgt von einem FIN , um das Schließen zu signalisieren. Die FIN wird von der anderen Seite bestätigt, wie jedes reguläre Paket 1 .

Wenn Sie warten müssen, bis die andere Seite ihren Sockel schließt, müssen Sie auf ihren FIN warten. Und zu erreichen , dass, Sie müssen erkennen Socket.getInputStream().read() < 0, was bedeutet , sollten Sie nicht nahe Ihrer Steckdose, wie es wäre seine schließenInputStream .

Nach dem, was ich in C und jetzt in Java getan habe, sollte das Erreichen eines solchen synchronisierten Abschlusses folgendermaßen erfolgen:

  1. Socket-Ausgang herunterfahren (sendet FIN am anderen Ende, dies ist das Letzte, was jemals von diesem Socket gesendet wird). Der Eingang ist noch offen, sodass Sie read()die Fernbedienung erkennen könnenclose()
  2. Lesen Sie den Socket, InputStreambis wir die Antwort-FIN vom anderen Ende erhalten (da sie die FIN erkennt, durchläuft sie denselben ordnungsgemäßen Verbindungsvorgang). Dies ist bei einigen Betriebssystemen wichtig, da sie den Socket nicht schließen, solange einer seiner Puffer noch Daten enthält. Sie werden als "Ghost" -Socket bezeichnet und verbrauchen Deskriptornummern im Betriebssystem (das ist bei modernen Betriebssystemen möglicherweise kein Problem mehr).
  3. Schließen Sie den Socket (indem Sie entweder anrufen Socket.close()oder schließen InputStreamoder OutputStream)

Wie im folgenden Java-Snippet gezeigt:

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
}

Natürlich beide Seiten müssen die Schließung die gleiche Weise verwenden oder das Sendeteil immer genügend Daten aussenden könnte das halte whileSchleife beschäftigt (zB wenn das Sendeteil nur Daten sendet und nie Verbindungsabbruch Lese zu erkennen. Welche ungeschickt ist, aber Sie haben möglicherweise keine Kontrolle darüber).

Wie @WarrenDew in seinem Kommentar hervorhob, führt das Verwerfen der Daten im Programm (Anwendungsschicht) zu einer nicht ordnungsgemäßen Trennung auf Anwendungsebene: obwohl alle Daten auf TCP-Ebene empfangen wurden (die while Schleife) , werden sie verworfen.

1 : Aus " Fundamental Networking in Java ": siehe Abb. 3.3 S.45 und der gesamte §3.7, S. 43-48

Matthieu
quelle
Java führt einen eleganten Abschluss durch. Es ist nicht "brutal".
Marquis von Lorne
2
@EJP, "Graceful Disconnection" ist ein spezifischer Austausch auf TCP-Ebene, bei dem der Client seinen Willen zum Trennen der Verbindung zum Server signalisieren sollte, der seinerseits die verbleibenden Daten sendet, bevor er seine Seite schließt. Der Teil "Restliche Daten senden" muss vom Programm verarbeitet werden (obwohl die Leute die meiste Zeit nichts senden würden). Das Anrufen socket.close()ist "brutal", da es diese Client / Server-Signalisierung nicht berücksichtigt. Der Server wird nur dann über eine Client-Trennung benachrichtigt, wenn sein eigener Socket-Ausgabepuffer voll ist (da keine Daten von der anderen Seite bestätigt werden, die geschlossen wurde).
Matthieu
Weitere Informationen finden Sie unter MSDN .
Matthieu
Java erzwingt nicht, dass Anwendungen Socket.close () aufrufen, anstatt zuerst die verbleibenden Daten zu senden, und verhindert nicht, dass sie zuerst herunterfahren. Socket.close () macht genau das, was close (sd) macht. Java unterscheidet sich in dieser Hinsicht nicht von der Berkeley Sockets API selbst. In der Tat in den meisten Hinsichten.
Marquis von Lorne
2
@LeonidUsov Das ist einfach falsch. Java read()gibt am Ende des Streams -1 zurück und tut dies auch weiterhin, wie oft Sie es aufrufen. AC read()oder recv()gibt am Ende des Streams Null zurück und tut dies auch weiterhin, wie oft Sie es aufrufen.
Marquis von Lorne
18

Ich denke, das ist eher eine Frage der Socket-Programmierung. Java folgt nur der Tradition der Socket-Programmierung.

Aus Wikipedia :

TCP bietet eine zuverlässige, geordnete Übermittlung eines Bytestroms von einem Programm auf einem Computer an ein anderes Programm auf einem anderen Computer.

Sobald der Handshake abgeschlossen ist, unterscheidet TCP nicht mehr zwischen zwei Endpunkten (Client und Server). Die Begriffe "Client" und "Server" dienen hauptsächlich der Vereinfachung. Der "Server" könnte also Daten senden, und der "Client" könnte einige andere Daten gleichzeitig aneinander senden.

Der Begriff "Schließen" ist ebenfalls irreführend. Es gibt nur eine FIN-Erklärung, was bedeutet: "Ich werde Ihnen keine weiteren Sachen schicken." Dies bedeutet jedoch nicht, dass sich keine Pakete im Flug befinden oder der andere nichts mehr zu sagen hat. Wenn Sie Schneckenpost als Datenverbindungsschicht implementieren oder wenn Ihr Paket unterschiedliche Routen zurückgelegt hat, empfängt der Empfänger möglicherweise Pakete in falscher Reihenfolge. TCP weiß, wie Sie dies für Sie beheben können.

Außerdem haben Sie als Programm möglicherweise keine Zeit, weiter zu überprüfen, was sich im Puffer befindet. So können Sie nach Belieben überprüfen, was sich im Puffer befindet. Alles in allem ist die aktuelle Socket-Implementierung nicht so schlecht. Wenn es tatsächlich isPeerClosed () gab, ist dies ein zusätzlicher Aufruf, den Sie jedes Mal tätigen müssen, wenn Sie read aufrufen möchten.

Eugene Yokota
quelle
1
Ich glaube nicht, Sie können sowohl unter Windows als auch unter Linux auf Zustände im C-Code testen !!! Java kann aus irgendeinem Grund einige der Dinge nicht verfügbar machen, wie es schön wäre, die Funktionen von getsockopt unter Windows und Linux verfügbar zu machen. Tatsächlich enthalten die folgenden Antworten einen Linux-C-Code für die Linux-Seite.
Dean Hiller
Ich glaube nicht, dass Sie mit der Methode 'isPeerClosed ()' verpflichtet sind, sie vor jedem Leseversuch aufzurufen. Sie können es einfach nur aufrufen, wenn Sie es ausdrücklich benötigen. Ich bin damit einverstanden, dass die aktuelle Socket-Implementierung nicht so schlecht ist, obwohl Sie in den Ausgabestream schreiben müssen, wenn Sie herausfinden möchten, ob der entfernte Teil des Sockets geschlossen ist oder nicht. Denn wenn dies nicht der Fall ist, müssen Sie Ihre schriftlichen Daten auch auf der anderen Seite verarbeiten, und es ist einfach genauso angenehm, auf dem vertikal ausgerichteten Nagel zu sitzen;)
Jan Hruby
1
Es tut mean ‚kein weiteres Paket im Flug‘. Die FIN wird nach allen Daten im Flug empfangen . Dies bedeutet jedoch nicht , dass der Peer den Socket für die Eingabe geschlossen hat . Sie müssen ** etwas senden * und einen 'Verbindungs-Reset' erhalten, um dies zu erkennen. Das FIN hätte nur ein Herunterfahren für die Ausgabe bedeuten können.
Marquis von Lorne
11

Die zugrunde liegende Sockets-API verfügt nicht über eine solche Benachrichtigung.

Der sendende TCP-Stapel sendet das FIN-Bit ohnehin erst beim letzten Paket, sodass möglicherweise viele Daten gepuffert werden, wenn die sendende Anwendung ihren Socket logisch geschlossen hat, bevor diese Daten überhaupt gesendet werden. Ebenso können Daten, die gepuffert sind, weil das Netzwerk schneller als die empfangende Anwendung ist (ich weiß nicht, vielleicht leiten Sie sie über eine langsamere Verbindung weiter), für den Empfänger von Bedeutung sein und Sie möchten nicht, dass die empfangende Anwendung sie verwirft nur weil das FIN-Bit vom Stapel empfangen wurde.

Mike Dimmick
quelle
In meinem Testbeispiel (vielleicht sollte ich hier eines angeben ...) wurden absichtlich keine Daten über die Verbindung gesendet / empfangen. Ich bin mir also ziemlich sicher, dass der Stack die FIN (ordnungsgemäß) oder RST (in einigen nicht ordnungsgemäßen Szenarien) erhält. Dies wird auch von netstat bestätigt.
Alexander
1
Sicher - wenn nichts gepuffert ist, wird die FIN sofort auf einem ansonsten leeren Paket (keine Nutzdaten) gesendet. Nach FIN werden jedoch an diesem Ende der Verbindung keine Datenpakete mehr gesendet (es wird weiterhin alles bestätigt, was an diese gesendet wurde).
Mike Dimmick
1
Was passiert ist, dass die Seiten der Verbindung in <code> CLOSE_WAIT </ code> und <code> FIN_WAIT_2 </ code> enden und in diesem Zustand <code> isConcected () </ code> und <code> sind isClosed () </ code> sieht immer noch nicht, dass die Verbindung beendet wurde.
Alexander
Vielen Dank für Ihre Vorschläge! Ich denke, ich verstehe das Problem jetzt besser. Ich habe die Frage präzisiert (siehe dritter Absatz): Warum gibt es nicht "Socket.isClosing", um auf halb geschlossene Verbindungen zu testen?
Alexander
8

Da bisher keine der Antworten die Frage vollständig beantwortet, fasse ich mein derzeitiges Verständnis des Problems zusammen.

Wenn eine TCP-Verbindung hergestellt wird und ein Peer anruft close()oder shutdownOutput()an seinem Socket, wechselt der Socket auf der anderen Seite der Verbindung in den CLOSE_WAITStatus. Grundsätzlich kann aus dem TCP-Stack ermittelt werden, ob sich ein Socket im CLOSE_WAITStatus befindet, ohne ihn aufzurufen read/recv(z. B. getsockopt()unter Linux: http://www.developerweb.net/forum/showthread.php?t=4395 ), aber das ist nicht der Fall tragbar.

Javas SocketKlasse scheint so konzipiert zu sein, dass sie eine Abstraktion bietet, die mit einem BSD-TCP-Socket vergleichbar ist, wahrscheinlich weil dies die Abstraktionsebene ist, an die Benutzer beim Programmieren von TCP / IP-Anwendungen gewöhnt sind. BSD-Sockets sind eine Verallgemeinerung, die andere Sockets als nur INET-Sockets (z. B. TCP) unterstützt. Sie bieten daher keine tragbare Möglichkeit, den TCP-Status eines Sockets zu ermitteln.

Es gibt keine Methode wie diese, isCloseWait()da Benutzer, die TCP-Anwendungen auf der von BSD-Sockets angebotenen Abstraktionsebene programmieren, nicht erwarten, dass Java zusätzliche Methoden bereitstellt.

Alexander
quelle
3
Ebenso wenig kann Java bietet keine zusätzliche Methoden, portabel. Vielleicht könnten sie eine isCloseWait () -Methode erstellen, die false zurückgibt, wenn die Plattform dies nicht unterstützt, aber wie viele Programmierer würden von diesem Gotcha gebissen, wenn sie nur auf unterstützten Plattformen testen würden?
Ephemient
1
Es sieht so aus, als ob es für mich portabel sein könnte ... Windows hat diese msdn.microsoft.com/en-us/library/windows/desktop/… und Linux diese pubs.opengroup.org/onlinepubs/009695399/functions/…
Dean Hiller
1
Es ist nicht so, dass Programmierer daran gewöhnt sind; Es ist so, dass die Socket-Schnittstelle für Programmierer nützlich ist. Beachten Sie, dass die Socket-Abstraktion nicht nur für das TCP-Protokoll verwendet wird.
Warren Dew
In Java gibt es keine Methode wie diese, isCloseWait()da sie nicht auf allen Plattformen unterstützt wird.
Marquis von Lorne
Das Protokoll ident (RFC 1413) ermöglicht es dem Server, die Verbindung entweder nach dem Senden einer Antwort offen zu halten oder sie zu schließen, ohne weitere Daten zu senden. Ein Java Ident-Client kann die Verbindung offen halten, um den 3-Wege-Handshake bei der nächsten Suche zu vermeiden. Woher weiß er jedoch, dass die Verbindung noch offen ist? Sollte es nur versuchen, auf einen Fehler zu reagieren, indem Sie die Verbindung erneut öffnen? Oder ist das ein Protokolldesignfehler?
David
7

Mit der Methode java.net.Socket.sendUrgentData (int) können Sie feststellen, ob die Remote-Seite einer (TCP) Socket-Verbindung geschlossen wurde, und die IOException abfangen, die ausgelöst wird, wenn die Remote-Seite nicht verfügbar ist. Dies wurde zwischen Java-Java und Java-C getestet.

Dies vermeidet das Problem des Entwurfs des Kommunikationsprotokolls zur Verwendung eines Ping-Mechanismus. Durch Deaktivieren von OOBInline an einem Socket (setOOBInline (false) werden alle empfangenen OOB-Daten stillschweigend verworfen, OOB-Daten können jedoch weiterhin gesendet werden. Wenn die Remote-Seite geschlossen wird, wird ein Verbindungsrücksetzen versucht, schlägt fehl und es wird eine IOException ausgelöst .

Wenn Sie tatsächlich OOB-Daten in Ihrem Protokoll verwenden, kann Ihr Kilometerstand variieren.

Onkel Per
quelle
4

Der Java IO-Stack sendet definitiv FIN, wenn er bei einem plötzlichen Herunterfahren zerstört wird. Es macht einfach keinen Sinn, dass Sie dies nicht erkennen können. Die meisten Clients senden die FIN nur, wenn sie die Verbindung beenden.

... ein weiterer Grund, warum ich die NIO Java-Klassen wirklich hasse. Es scheint, als wäre alles ein bisschen halbherzig.


quelle
1
Außerdem scheint es, dass ich beim Lesen (-1 return) nur dann ein Stream-Ende erhalte, wenn eine FIN vorhanden ist. Dies ist also die einzige Möglichkeit, ein Herunterfahren auf der Leseseite zu erkennen.
3
Sie können es erkennen. Sie erhalten EOS beim Lesen. Und Java sendet keine FINs. TCP macht das. Java implementiert kein TCP / IP, sondern nur die Plattformimplementierung.
Marquis von Lorne
4

Es ist ein interessantes Thema. Ich habe gerade den Java-Code durchgesehen, um ihn zu überprüfen. Nach meiner Feststellung gibt es zwei unterschiedliche Probleme: Das erste ist der TCP-RFC selbst, der es ermöglicht, dass ferngesteuerte Sockets Daten im Halbduplex übertragen, sodass ein entfernt geschlossener Socket immer noch halb offen ist. Gemäß RFC schließt RST die Verbindung nicht. Sie müssen einen expliziten ABORT-Befehl senden. Java ermöglicht also das Senden von Daten über einen halb geschlossenen Socket

(Es gibt zwei Methoden zum Lesen des Schließstatus an beiden Endpunkten.)

Das andere Problem ist, dass die Implementierung angibt, dass dieses Verhalten optional ist. Da Java bestrebt ist, portabel zu sein, haben sie die beste gemeinsame Funktion implementiert. Die Pflege einer Karte von (Betriebssystem, Implementierung von Halbduplex) wäre wohl ein Problem gewesen.

Lorenzo Boccaccia
quelle
1
Ich nehme an, Sie sprechen von RFC 793 ( faqs.org/rfcs/rfc793.html ). Abschnitt 3.5 Schließen einer Verbindung. Ich bin nicht sicher, ob dies das Problem erklärt, da beide Seiten das ordnungsgemäße Herunterfahren der Verbindung abschließen und in Zuständen landen, in denen sie keine Daten senden / empfangen sollten.
Alexander
Hängt davon ab. Wie viele FIN sehen Sie auf der Steckdose? Dies könnte auch ein plattformspezifisches Problem sein: Möglicherweise antwortet Windows jeder FIN mit einer FIN und die Verbindung ist an beiden Enden geschlossen, aber andere Betriebssysteme verhalten sich möglicherweise nicht so, und hier tritt Problem 2 auf
Lorenzo Boccaccia,
1
Nein, das ist leider nicht der Fall. isOutputShutdown und isInputShutdown sind das erste, was jeder versucht, wenn er mit dieser "Entdeckung" konfrontiert wird, aber beide Methoden geben false zurück. Ich habe es gerade unter Windows XP und Linux 2.6 getestet. Die Rückgabewerte aller 4 Methoden bleiben auch nach einem Leseversuch gleich
Alexander
1
Für die Aufzeichnung ist dies kein Halbduplex. Halbduplex bedeutet, dass nur eine Seite gleichzeitig senden kann. beide seiten können noch senden.
Rbp
1
isInputShutdown und isOutputShutdown testen das lokale Ende der Verbindung. Hierbei handelt es sich um Tests, um herauszufinden, ob Sie für diesen Socket shutdownInput oder shutdownOutput aufgerufen haben. Sie erzählen Ihnen nichts über das entfernte Ende der Verbindung.
Mike Dimmick
3

Dies ist ein Fehler der OO-Socket-Klassen von Java (und allen anderen, die ich mir angesehen habe) - kein Zugriff auf den ausgewählten Systemaufruf.

Richtige Antwort in 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 */  
}  
Joshua
quelle
Sie können das gleiche mit tun getsocketop(... SOL_SOCKET, SO_ERROR, ...).
David Schwartz
1
Der Fehlerdateideskriptorsatz zeigt keine geschlossene Verbindung an. Bitte lesen Sie das ausgewählte Handbuch: 'exceptionfds - Dieses Set wird auf "außergewöhnliche Bedingungen" überwacht. In der Praxis ist nur eine solche Ausnahmebedingung üblich: die Verfügbarkeit von OOB-Daten (Out-of-Band) zum Lesen von einem TCP-Socket. ' FIN sind keine OOB-Daten.
Jan Wrobel
1
Sie können die Klasse 'Selector' verwenden, um auf den Systemaufruf 'select ()' zuzugreifen. Es wird jedoch NIO verwendet.
Matthieu
1
Es gibt nichts Außergewöhnliches an einer Verbindung, die von der anderen Seite unterbrochen wurde.
David Schwartz
1
Viele Plattformen, einschließlich Java, tun Zugriff auf die select () Systemaufruf zur Verfügung stellen.
Marquis von Lorne
2

Hier ist eine lahme Problemumgehung. Verwenden Sie SSL;) und SSL führt beim Herunterfahren einen engen Handshake durch, sodass Sie benachrichtigt werden, dass der Socket geschlossen wird (die meisten Implementierungen scheinen einen eigentlichen Handshake-Teardown durchzuführen).

Dean Hiller
quelle
2
Wie wird man "benachrichtigt", wenn der Socket geschlossen wird, wenn SSL in Java verwendet wird?
Dave B
2

Der Grund für dieses Verhalten (das nicht Java-spezifisch ist) ist die Tatsache, dass Sie keine Statusinformationen vom TCP-Stapel erhalten. Schließlich ist ein Socket nur ein weiteres Dateihandle, und Sie können nicht herausfinden, ob tatsächlich Daten zum Lesen vorhanden sind, ohne es tatsächlich zu versuchen ( select(2)dies hilft nicht, es signalisiert nur, dass Sie es versuchen können, ohne es zu blockieren).

Weitere Informationen finden Sie in den häufig gestellten Fragen zu Unix-Sockets .

WMR
quelle
2
REALbasic-Sockets (unter Mac OS X und Linux) basieren auf BSD-Sockets, aber RB kann Ihnen einen netten Fehler 102 geben, wenn die Verbindung am anderen Ende unterbrochen wird. Ich stimme dem Originalposter zu, dies sollte möglich sein und es ist lahm, dass Java (und Cocoa) es nicht bereitstellen.
Joe Strout
1
@ JoeStrout RB kann das nur, wenn Sie einige E / A ausführen. Es gibt keine API, die Ihnen den Status der Verbindung ohne E / A anzeigt. Zeitraum. Dies ist kein Java-Mangel. Dies ist in der Tat auf das Fehlen eines "Wähltons" in TCP zurückzuführen, was ein bewusstes Designmerkmal ist.
Marquis von Lorne
select() Zeigt an, ob Daten oder EOS zum Lesen verfügbar sind, ohne zu blockieren. "Signale, die Sie versuchen können, ohne zu blockieren" ist bedeutungslos. Wenn Sie sich im nicht blockierenden Modus befinden, können Sie es jederzeit ohne Blockierung versuchen. select()wird von Daten im Socket-Empfangspuffer oder einer ausstehenden FIN oder einem Leerzeichen im Socket-Sendepuffer gesteuert.
Marquis von Lorne
@EJP Was ist mit getsockopt(SO_ERROR)? In der Tat wird sogar getpeernameangezeigt, ob die Steckdose noch angeschlossen ist.
David Schwartz
0

Nur Schreibvorgänge erfordern den Austausch von Paketen, wodurch der Verbindungsverlust festgestellt werden kann. Eine übliche Problemumgehung ist die Verwendung der Option KEEP ALIVE.

Strahl
quelle
Ich denke, ein Endpunkt kann ein ordnungsgemäßes Herunterfahren der Verbindung initiieren, indem er ein Paket mit gesetztem FIN sendet, ohne Nutzdaten zu schreiben.
Alexander
@Alexander Natürlich, aber das ist für diese Antwort nicht relevant, bei der es darum geht , weniger Verbindungen zu erkennen.
Marquis von Lorne
-3

Wenn es um halboffene Java-Sockets geht, sollten Sie sich isInputShutdown () und isOutputShutdown () ansehen .

JimmyB
quelle
Nein. Das sagt Ihnen nur, was Sie angerufen haben, nicht, was der Peer angerufen hat.
Marquis von Lorne
Möchten Sie Ihre Quelle für diese Aussage mitteilen?
JimmyB
3
Möchten Sie Ihre Quelle für das Gegenteil teilen? Es ist deine Aussage. Wenn Sie einen Beweis haben, lassen Sie es uns haben. Ich behaupte, dass Sie falsch sind. Mach das Experiment und beweise mir das Gegenteil.
Marquis von Lorne
Drei Jahre später und kein Experiment. QED
Marquis von Lorne