Wie behebt man java.net.SocketException: Rohrbruch?


150

Ich verwende den http-Client von Apache Commons, um die URL mithilfe der Post-Methode aufzurufen, um die Parameter zu veröffentlichen, und es wird selten der folgende Fehler ausgegeben.

java.net.SocketException: Broken pipe
        at java.net.SocketOutputStream.socketWrite0(Native Method)
        at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:92)
        at java.net.SocketOutputStream.write(SocketOutputStream.java:136)
        at java.io.BufferedOutputStream.write(BufferedOutputStream.java:105)
        at java.io.FilterOutputStream.write(FilterOutputStream.java:80)
        at org.apache.commons.httpclient.methods.ByteArrayRequestEntity.writeRequest(ByteArrayRequestEntity.java:90)
        at org.apache.commons.httpclient.methods.EntityEnclosingMethod.writeRequestBody(EntityEnclosingMethod.java:499)
        at org.apache.commons.httpclient.HttpMethodBase.writeRequest(HttpMethodBase.java:2114)
        at org.apache.commons.httpclient.HttpMethodBase.execute(HttpMethodBase.java:1096)
        at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:398)

Kann jemand vorschlagen, was diese Ausnahme verursacht und wie man sie debuggt?


Antworten:


81

Dies wird verursacht durch:

  • In der Regel wird an eine Verbindung geschrieben, wenn das andere Ende sie bereits geschlossen hat.
  • In der Regel schließt der Peer die Verbindung, ohne alle Daten zu lesen, die an seinem Ende bereits ausstehen.

In beiden Fällen haben Sie also ein schlecht definiertes oder implementiertes Anwendungsprotokoll.

Es gibt einen dritten Grund, den ich hier nicht dokumentieren werde, bei dem der Peer jedoch absichtlich Maßnahmen ergreift, um die Verbindung zurückzusetzen, anstatt sie ordnungsgemäß zu schließen.


4
Können Sie mir bitte helfen, es zu identifizieren und zu korrigieren? Wie kann man den Grund bestätigen und beheben?

4
Ich habe den Grund bestätigt. Sie schreiben, während das andere Ende die Verbindung bereits geschlossen hat. Das Update ist nicht das zu tun. Da das andere Ende es nicht liest, hat es sowieso keinen Sinn. Wie ich auch sagte, wenn dies passiert, stimmt etwas mit Ihrer Anwendungsprotokollspezifikation oder -implementierung nicht, höchstwahrscheinlich haben Sie nicht einmal eines.
Marquis von Lorne

26
Wenn die serverseitige HTTP-Anwendung Broken Pipe-Ausnahmen erhält, bedeutet dies nur, dass der Client-Browser beendet wurde / zu einer anderen Seite gewechselt ist / eine Zeitüberschreitung aufgetreten ist / im Verlauf zurückgegangen ist / was auch immer. Vergiss es einfach.
Marquis von Lorne

3
Wir haben diese Ausnahme beim Abbrechen einer Ajax-Anfrage im Browser (mit XMLHttpRequest.abort()) gesehen.
Obecker

2
Eine mögliche Ursache wäre ein Proxy oder ein HTTP-Server, der die Verbindung zu früh schließt. Zum Beispiel ein Apache-HTTP-Server zwischen Ihrem J2EE-Server und den Anwendungsclients mit einem kurzen definierten Zeitlimit. Zumindest geschah dies in unserer Produktionsumgebung. Kunden beschweren sich über nicht vollständig geladene Seiten und wir haben diese Fehler im JBoss-Protokoll gesehen. Nach einigen Tests stellen wir fest, dass das Problem ein schlecht konfigurierter HTTP-Server war.
Ricardo Vila

32

In unserem Fall haben wir dies bei einem Auslastungstest auf unserem App-Server festgestellt. Es stellte sich heraus, dass wir unserer JVM zusätzlichen Speicher hinzufügen müssen, da sie knapp wird. Dies löste das Problem.

Versuchen Sie, den für die JVM verfügbaren Speicher zu erhöhen, oder überwachen Sie die Speichernutzung, wenn diese Fehler auftreten.


4
Ich habe das gleiche Problem während eines Belastungstests bekommen. Ich denke, die Generierung der Daten nimmt viel Zeit in Anspruch und das Lasttest-Tool wartet die Daten nicht lange genug, um die Verbindung zu schließen. In Ihrem Fall hat das Hinzufügen von Speicher möglicherweise die Dauer des Datengenerierungsprozesses verkürzt, sodass beim Auslastungstest alle Daten ohne Zeitlimit abgerufen wurden. Eine Alternative zum Erhöhen des Speichers wäre gewesen, das Zeitlimit für das Lasttestwerkzeug zu erhöhen.
Julien Kronegg

2
Offensichtlich hat der niedrige Speicher dazu geführt, dass die Anwendung den Empfangssocket geschlossen oder sogar ganz beendet hat, mit dem gleichen Effekt. Niedriger Speicher allein führt nicht zu Rohrbrüchen.
Marquis von Lorne

1
Dies scheint tatsächlich auch während unserer Schwerlastanwendung ein Problem zu sein
Ruben de Vries

7

SocketException: Eine unterbrochene Pipe wird dadurch verursacht, dass das 'andere Ende' (der Client oder der Server) die Verbindung schließt, während Ihr Code entweder von der Verbindung liest oder in diese schreibt.

Dies ist eine sehr häufige Ausnahme in Client / Server-Anwendungen, die Datenverkehr von Clients oder Servern außerhalb der Anwendungssteuerung empfangen. Der Client ist beispielsweise ein Browser. Wenn der Browser einen Ajax-Anruf tätigt und / oder der Benutzer die Seite oder den Browser einfach schließt, kann dies die gesamte Kommunikation unerwartet beenden. Grundsätzlich wird dieser Fehler jedes Mal angezeigt, wenn das andere Ende seine Anwendung beendet, und Sie haben ihn nicht erwartet.

Wenn diese Ausnahme in Ihrer Anwendung auftritt, sollten Sie Ihren Code überprüfen, in dem die E / A (Eingabe / Ausgabe) auftritt, und ihn mit einem Try / Catch-Block umschließen, um diese E / A-Ausnahme abzufangen. Es liegt dann an Ihnen, zu entscheiden, wie Sie mit dieser halbgültigen Situation umgehen möchten.

In Ihrem Fall ist der Anruf an HttpMethodDirector.executeWithRetry- der früheste Ort, an dem Sie noch die Kontrolle haben - Stellen Sie also sicher, dass der Anruf mit dem Try / Catch-Block abgeschlossen ist, und behandeln Sie ihn so, wie Sie es für richtig halten.

Ich würde dringend davon abraten, SocketException-Broken Pipe-spezifische Fehler auf anderen als Debug- / Trace-Ebenen zu protokollieren. Andernfalls kann dies als eine Form von DOS-Angriff (Denial Of Service) verwendet werden, indem die Protokolle ausgefüllt werden. Versuchen Sie, Ihre Anwendung für dieses häufige Szenario zu härten und negativ zu testen.


Dies wird nicht dadurch verursacht, dass der Peer die Verbindung schließt, während Sie daraus lesen. Dies führt zu einem anderen Zustand am Ende des Streams, der oft überhaupt keine Ausnahme darstellt.
Marquis von Lorne

5

Alle offenen Streams und Verbindungen müssen ordnungsgemäß geschlossen sein. Wenn wir das nächste Mal versuchen, das urlConnection-Objekt zu verwenden, wird kein Fehler ausgegeben. Als Beispiel hat die folgende Codeänderung den Fehler für mich behoben.

Vor:

OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out));
bw.write("Some text");
bw.close();
out.close();

Nach dem:

OutputStream os = urlConnection.getOutputStream();
OutputStream out = new BufferedOutputStream(os);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out));
bw.write("Some text");
bw.close();
out.close();
os.close(); // This is a must.

8
Dies ist eigentlich sehr seltsam, da a BufferedOutputStreamimmer close()den zugrunde liegenden Ausgabestream (also den Ausgabestream von urlConnection) aufruft . Siehe docs.oracle.com/javase/6/docs/api/java/io/… und docs.oracle.com/javase/6/docs/api/java/io/…. Wenn Sie also anrufen os.close(), sollte es bereits geschlossen sein . Nein?
Obecker

4
'os.close ()' ist kein 'Muss'. Weder ist 'out.close ()' für diese Angelegenheit. 'bw.close ()' ist ausreichend. @obedker ist richtig. Diese Änderung allein kann das Problem nicht behoben haben.
Marquis von Lorne

1

Ich habe die Funktion zum Herunterladen von Daten über den FTP-Server implementiert und dort auch die gleiche Ausnahme festgestellt, als ich den Download fortgesetzt habe. Um diese Ausnahme zu beheben, müssen Sie immer die Verbindung zur vorherigen Sitzung trennen und eine neue Instanz des Clients sowie eine neue Verbindung zum Server erstellen. Dieser Ansatz könnte auch für HTTPClient hilfreich sein.


Um diesen Fehler zu beheben, müssen Sie vermeiden, geschlossene Sockets zu verwenden.
Marquis von Lorne

3
LOL. Sie könnten den ganzen Tag damit verschwenden, alle falschen Ratschläge zu SO zu korrigieren.
Dave

2
@ Dave in der Tat. Manchmal mache ich.
Marquis von Lorne

1

Ich hatte das gleiche Problem, als ich eine einfache Java-Anwendung entwickelte, die einen bestimmten TCP abhört. Normalerweise hatte ich kein Problem, aber als ich einen Stresstest durchführte, bemerkte ich, dass eine Verbindung mit einem Fehler unterbrochen wurde socket write exception.

Nach der Untersuchung habe ich eine Lösung gefunden, die mein Problem löst. Ich weiß, dass diese Frage ziemlich alt ist, aber ich teile meine Lösung lieber mit, jemand kann sie nützlich finden.

Das Problem war bei der ServerSocket-Erstellung. Ich habe aus Javadoc gelesen, dass es ein Standardlimit von 50 ausstehenden Sockets gibt. Wenn Sie versuchen, eine andere Verbindung herzustellen, werden diese abgelehnt. Die Lösung besteht einfach darin, diese Standardkonfiguration auf der Serverseite zu ändern. Im folgenden Fall erstelle ich einen Socket-Server, der den TCP-Port überwacht 10_000und maximal 200ausstehende Sockets akzeptiert .

new Thread(() -> {
      try (ServerSocket serverSocket = new ServerSocket(10_000, 200)) {
        logger.info("Server starts listening on TCP port {}", port);

        while (true) {
          try {
            ClientHandler clientHandler = clientHandlerProvider.getObject(serverSocket.accept(), this);
            executor.execute(clientHandler::start);
          } catch (Exception e) {
            logger.error(e.getMessage());
          }
        }

      } catch (IOException | SecurityException | IllegalArgumentException e) {
        logger.error("Could not open server on TCP port {}. Reason: {}", port, e.getMessage());
      }
    }).start();

Aus Javadoc von ServerSocket :

Die maximale Warteschlangenlänge für eingehende Verbindungsanzeigen (eine Verbindungsanforderung) wird auf den Parameter backlog festgelegt. Wenn eine Verbindungsanzeige eintrifft, wenn die Warteschlange voll ist, wird die Verbindung abgelehnt.


0

Das Problem könnte sein, dass Ihre bereitgestellten Dateien nicht mit den richtigen RMI-Methoden aktualisiert werden. Überprüfen Sie, ob Ihre RMI-Schnittstelle über aktualisierte Parameter oder aktualisierte Datenstrukturen verfügt, über die Ihr Client nicht verfügt. Oder dass Ihr RMI-Client keine Parameter hat, die sich von denen Ihrer Serverversion unterscheiden.

Dies ist nur eine fundierte Vermutung. Nach dem erneuten Bereitstellen der Klassendateien meiner Serveranwendung und dem erneuten Testen ist das Problem "Broken Pipe" behoben.


Welche RMI-Methoden? RMI wird in der Frage nicht erwähnt.
Marquis von Lorne

0

Die obigen Antworten veranschaulichen den Grund dafür java.net.SocketException: Broken pipe: Das andere Ende hat die Verbindung geschlossen. Ich möchte Erfahrungen austauschen, die passiert sind, als ich darauf gestoßen bin:

  1. In der Anfrage eines Kunden wird der Content-TypeHeader fälschlicherweise größer gesetzt als der Anfragetext tatsächlich ist (tatsächlich gab es überhaupt keinen Text).
  2. Der unterste Dienst im Tomcat-Socket wartete auf Körperdaten dieser Größe (http befindet sich auf TCP, wodurch die Bereitstellung durch Kapselung und ... sichergestellt wird.)
  3. Wenn 60 Sekunden abgelaufen sind, löst Tomcat eine Timeout-Ausnahme aus: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception java.net.SocketTimeoutException: null
  4. Der Client erhält aufgrund der Timeout-Ausnahme eine Antwort mit dem Statuscode 500.
  5. Client-Verbindung schließen (weil es eine Antwort erhält).
  6. Kater wirft java.net.SocketException: Broken pipe weil der Client es geschlossen hat.

Manchmal löst Tomcat keine defekte Pip-Ausnahme aus, da eine Timeout-Ausnahme die Verbindung schließt, weshalb mich ein solcher Unterschied auch verwirrt.


Der Content-TypeHeader gibt keine Zahl an, daher kann er nicht größer als alles andere sein. Timeout-Ausnahmen schließen den Socket nicht. Antwort macht überhaupt keinen Sinn.
Marquis von Lorne

@EJP Vielleicht war es Inhaltslänge, erinnere mich nicht. Ich denke, das Timeout in Tomcat führt dazu, dass 500 zurückgegeben werden, während der Client diese Antwort empfängt und die Verbindung schließt, was schließlich dazu führt, dass Tomcat nicht wie erwartet genügend Bytes empfängt.
Tiina

Wenn Tomcat 500 sendet, hat es bereits alles erhalten, was es plant. Macht immer noch keinen Sinn.
Marquis von Lorne

@ user207421 nicht wirklich. Tomcat sendet 500, weil es nicht alle erwarteten Daten empfängt, sondern eine Zeitüberschreitung aufweist.
Tiina

-1

JavaDoc:

Die maximale Warteschlangenlänge für eingehende Verbindungsanzeigen (eine Verbindungsanforderung) ist auf 50 festgelegt. Wenn eine Verbindungsanzeige eintrifft, wenn die Warteschlange voll ist, wird die Verbindung abgelehnt.

Sie sollten beispielsweise den Parameter "backlog" Ihres ServerSocket erhöhen

int backlogSize = 50 ;
new ServerSocket(port, backlogSize);

1
Durch Erhöhen des Rückstands wird ein Fehler "Rohrbruch" nicht behoben.
Marquis von Lorne

1
aber es hat mir geholfen
TheSecond

@EJP Ich habe einen Server unter Linux getestet und es hat funktioniert, aber unter Mac OS - nicht. Und die Erhöhung des Auftragsbestands hat mir geholfen. Ich wusste nicht, dass die maximale Größe 50 ist.
TheSecond

1
Du hast etwas anderes getestet. Listen Backlog hat nichts mit dieser Ausnahme zu tun. Es hilft bei Verbindungsverweigerungen oder Zeitüberschreitungen beim Client. Keine "Socket Closed" -Ausnahmen, bei denen es sich um einen lokalen Fehler handelt.
Marquis von Lorne

Für Socket-Pools (z. B. einen HTTP-Server wie Tomcat) gibt es Konfigurationseinstellungen für die Anzahl der ausstehenden "Akzeptierungen", die der Server vor dem Versenden von Wartungsthreads "in die Warteschlange stellt", und eine separate Einstellung für die Anzahl der Threads im Pool. Nichts davon hängt jedoch mit dem Problem zusammen, dass nach dem "Akzeptieren ()" des Servers beim Herstellen der Verbindung der Ausgabestream plötzlich (unabsichtlich) "geschlossen" wird.
Darrell Teague

-1

Die Ursache ist, dass der Remote-Peer seinen Socket schließt (z. B. Absturz). Wenn Sie also beispielsweise versuchen, Daten in ihn zu schreiben, erhalten Sie dies. Um dies zu lösen, schützen Sie Lese- / Schreibvorgänge für Socket-Vorgänge zwischen (try catch) und verwalten Sie dann den Fall des Verbindungsverlusts im catch (Verbindung erneuern, Programm stoppen usw.):

 try {
      /* your code */
 } catch (SocketException e) {
      e.printStackTrace();
      /* insert your failure processings here */
 }

3
Dadurch werden die Probleme protokolliert , aber nicht gelöst . Lösen sie müssen Sie (a) lösen alle möglichen Anwendungsprotokollfehler und (b) schließen tote Verbindungen. Nicht nur Protokollausnahmen, von denen die meisten schwerwiegend sind.
Marquis von Lorne

@EJP: Wenn Sie einen Fehler feststellen, sollten Sie natürlich Ihren Socket (Kontext) in der catch-Anweisung bereinigen. Ich kann nicht erraten, was der Entwickler tun wird. es liegt in seiner eigenen
Hand,

Es ist Sache des Entwicklers, das Problem des Anwendungsprotokolls zu beheben, das den Fehler verursacht. Nichts ich Ihre Antwort oder Code, der ihm hilft. tun Sie dies. 'Lese- / Schreibvorgänge einfügen zwischen' behebt das Problem nicht. Deine Antwort ist falsch.
Marquis von Lorne

@ user207421, der Entwickler fragt, wie das kaputte Rohr repariert werden soll. Ich habe ihm angezeigt, zunächst sein Programm mit (try catch) zu schützen. Dadurch wird ein Absturz des Programms vermieden. dann füge ich einen Kommentar ein, um ihm anzuzeigen, dass er eine saubere Verarbeitung durchführen soll. Generell gibt es bei einem Rohrbruch viele Alternativen, ich kann das Ziel des Programms nicht erraten, Programm stoppen, Verbindung erneuern, Daten sind beschädigt oder nicht .... etc. Es ist Sache des Programmierers, zu entscheiden, welche Option zu tun ist. Was war los mit meiner Antwort?
Elhadi dp ıpɐɥ ן 27
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.