SocketException trotz try catch

Hallo liebe Experten,

seit längerer Zeit ist ein Servlet bei mir in Betrieb.
Es ist recht komplex.
Indem definierte Bereiche in try-catch-Blöcke eingeschlossen und die Fehler differenziert wurden, sind jetzt eigentlich alle Bugs gefunden und beseitigt.

Mit einer Ausnahme.

In Abständen kommt es zu einer:
java.net.SocketException: Broken pipe

Auslöser: out.close();

Der Inhalt der Methode doGet() ist ebenfalls in einem try-catch-Block (Exception e) eingeschlossen. Nun sollte man meinen, dass eine SocketException, die von Exception abgeleitet ist, in diesem try-catch-Block abgefangen wird.

Tut sie aber nicht.

Tatsächlich erscheint sie in der Konsole.
Weiß jemand, was da geschieht?

Vielen Dank für die Mühe
und tschüs
Uwe

hi Uwe

hi uwe

eine broken pipe bekommst du dann, wenn beim schreiben auf den socket der noch offen war, beim abschliessenden flush das gegenüber aber den socket bereits abgebrochen hat. das tritt vor allem dann auf, wenn über den outputstream noch ein bufferedoutputstream gelegt wird. bei servlets ist das phänomen recht häufig - der internet explorer der gegenseite hat den socket schon längst geschlossen, obwohl dein servlet noch daten schreibt.

wenn du halbwegs sauber programmierst, hast du deine normale logik im try-block drinnen, im catch-block die fehlerbehandlung und im finally-block das socket.close(). der fehler tritt aber erst beim close auf und nicht früher (wie gesagt, zum zeitpunkt des write() war der socket ja noch in ordnung, nur wurde aufgrund des buffers noch nichts über die datenleitung geschickt). wenn du nun im finally-block nicht nochmal ein try-catch drüberlegst und die betroffene methode sogar noch als throws irgendwasexception deklarierst, dann bricht eben der thread mit einem stacktrace auf der konsole ab.

lg
erwin

Hallo Erwin,

eine broken pipe bekommst du dann, wenn beim schreiben auf den
socket der noch offen war, beim abschliessenden flush das
gegenüber aber den socket bereits abgebrochen hat. das tritt
vor allem dann auf, wenn über den outputstream noch ein
bufferedoutputstream gelegt wird. bei servlets ist das
phänomen recht häufig - der internet explorer der gegenseite
hat den socket schon längst geschlossen, obwohl dein servlet
noch daten schreibt.

Bis hierhin habe ich dich verstanden.

wenn du halbwegs sauber programmierst, hast du deine normale
logik im try-block drinnen, im catch-block die
fehlerbehandlung

Genau so ist es jetzt bei mir.

und im finally-block das socket.close().

Eine finally-Klausel kommt noch nicht vor.
Der Datenstrom wird über

PrintWriter out = response.getWriter();

angefordert.
Wie komme ich an das Socket-Objekt?

wenn du nun im finally-block nicht
nochmal ein try-catch drüberlegst und die betroffene methode
sogar noch als throws irgendwasexception deklarierst, dann
bricht eben der thread mit einem stacktrace auf der konsole
ab.

Bisher dachte ich, die finally-Klausel ist ein Teil des try-catch-Blocks. Wieso kann man dann noch einen try-catch-Block darüber legen? Sicher habe ich dich falsch verstanden.

Also, jetzt die Zusammenfassung.
Try und catch sind offenbar o. k.
finally sollte socket schließen. Wo bekomme ich aber socket her?

Vielen Dank für die Mühe
und tschüs
Uwe

hallo uwe

Bisher dachte ich, die finally-Klausel ist ein Teil des
try-catch-Blocks. Wieso kann man dann noch einen
try-catch-Block darüber legen? Sicher habe ich dich falsch
verstanden.

stimmt insofern, als dass try-catch-finally immer zusammengehören. sie dürfen aber beliebig geschachtelt werden. manchmal ist das auch unbedingt notwendig.

geht also durchaus sowas:

try {
 // mach irgendwas...
 try {
 int x = Integer.parseInt(eingabe);
 } catch (NumberFormatException nfe) {
 // defaultwert annehmen
 x = 17 ;
 }
 // mache was anders..
 Socket s = new Socket(host,port);
 // und weiter
 s.flush() ;
} catch (Exception e) {
 // fehlermeldung ausgeben
} finally {
 try {
 s.close() ;
 } catch ( Exception e2 ) {
 // keine aktion sinnvoll...
 }
}

der finally-block wird IMMER ausgeführt - auch im fall einer exception. dementsprechend schließt man normalerweise im finally-block alle ressourcen, damit sie auch im fehlerfall geschlossen werden. da das close aber selbst wieder eine exception werfen kann, muss der befehl eben selbst wieder in einen try-catch-block rein, auch wenn der nicht sonderlich sinnvoll ist (wenn das close nicht geht, kannst du eh nix mehr machen).

natürlch kannst du das finally auch weglassen - im obigen fall, so ein string in einen int umgewandelt wird, hat das finally keinen nutzen.

was ich leider ignoriert habe, ist dass du ja mit servlets arbeitest und daher mit http-requests bzw. responses hantierst. hier greifst du nicht direkt auf den socket zu. statt dessen bekommst du den input- bzw. outputstream direkt zur verfügung gestellt. wenn du den schließt, ist der socket auch gleich zu. vorher natürlich das flush nicht vergessen!

alles klar nun?

lg
erwin

Hallo Erwin,

nur damit ich es richtig verstanden habe:

protected void doGet(HttpServletRequest request, HttpServletResponse response)
{ 
 try
 {
 PrintWriter out = response.getWriter();
 out.close();
 }
 catch (Exception e)
 {
 ;
 }
 finally
 {
 try
 {
 response.getOutputStream().flush();
 response.getOutputStream().close();
 }
 catch (Exception e)
 {
 ;
 }
 }
}

Habe ich dich richtig verstanden?

Danke
und tschüs
Uwe

nicht ganz…
hi uwe

naja, so nicht

dein out.close() schließt eh den socket ab. zusätzlich noch den outputsteam extra holen bringt nicht viel.

ausserdem kannst du dir das einsame „;“ im catch-block schenken - bringt nix

also so in etwa:

PrintWriter out = null ;
try {
 out = response.getWriter();
 out.println("ausgabe");
 out.flush();
} catch ( Exception e ) {
 e.printStackTrace();
} finally {
 try {
 out.close() ;
 } catch ( Exception e2) {
 }
}

ich würde bei der ersten exception zumindest den stacktrace ausgeben - sonst weisst du im fehlerfall nicht, was passiert ist. ein komplett leerer catch-block hat selten sinn. nur wenn der fehler tatsächlich uninteressant ist.

diskutieren kann man, ob das flush am ende des try-blocks oder im finally drinnen sein soll. problem: das flush kann auch eine exception werfen. in einem gemeinsamen try-catch-block im finally drinnen ist insofern unsauber, als bei einer exception im flush das close nicht durchgeführt wird. das flush am ende des try-blocks bedeutet aber auch, dass im fall einer exception vor dem close kein flush mehr kommt und daher ev. daten, die bereits geschrieben wurden aber noch im buffer stecken, nicht weitergeschickt werden. stellt sich nur die frage, ob das überhaupt funktionieren würde - meist bedeutet die exception ja, dass die verbindung abgebrochen ist.

auf der sicheren seite ist man mit:

...
} finally {
 try {
 out.flush() ;
 } catch ( Exception e2) {
 }
 try {
 out.close() ;
 } catch ( Exception e3) {
 }
}

macht den code nicht gerade lesbarer und bringt nur wenig. insofern wirst du dieses konstrukt wahrscheinlich selten finden…

lg
erwin

1 Like

Dafür ein Sternchen
Hallo Erwin,

das leuchtet mir ein.
Vielen Dank für deine Ausführungen.
Dafür gibt es ein Sternchen.

Auf den flush() habe ich verzichtet, da es im konkreten Fall egal ist, ob alle Daten übermittelt werden.

close() ist nun in der finally-Klausel und in einem try-catch-Block.
Das sollte genügen.

Für das neue Jahr wünsche ich dir viel Erfolg
und tschüs
Uwe
http://www.haller-mtl.de

also so in etwa:

PrintWriter out = null ;
try {
out = response.getWriter();
out.println(„ausgabe“);
out.flush();
} catch ( Exception e ) {
e.printStackTrace();
} finally {
try {
out.close() ;
} catch ( Exception e2) {
}
}