Come risolvere java.net.SocketException: Broken pipe?


150

Sto usando il client http di commons apache per chiamare url usando il metodo post per pubblicare i parametri e sta lanciando raramente l'errore di seguito.

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)

Qualcuno può suggerire cosa sta causando questa eccezione e come eseguirne il debug?


Risposte:


81

Ciò è causato da:

  • di solito, scrivendo su una connessione quando l'altra estremità l'ha già chiusa;
  • meno di solito, il peer chiude la connessione senza leggere tutti i dati già in sospeso alla sua fine.

Quindi in entrambi i casi hai un protocollo applicativo mal definito o implementato.

C'è un terzo motivo che non documenterò qui, ma che coinvolge il peer che intraprende azioni deliberate per ripristinare piuttosto che chiudere correttamente la connessione.


4
Potete per favore aiutarmi a identificarlo e correggerlo? Fondamentalmente, come confermare il motivo e risolvere?

4
Io ho confermato la ragione. Stai scrivendo mentre l'altra estremità ha già chiuso la connessione. La soluzione non è farlo. Dato che l'altra estremità non lo legge, non ha comunque senso. Come ho anche detto, se ciò accade, c'è qualcosa che non va nella specifica o nell'implementazione del protocollo dell'applicazione, molto probabilmente che non ne hai nemmeno uno.
Marchese di Lorne,

26
Se l'applicazione HTTP lato server sta ottenendo eccezioni Broken Pipe, significa semplicemente che il browser client è uscito / è passato a un'altra pagina / è scaduto / è tornato indietro nella cronologia / qualunque cosa. Dimenticalo e basta.
Marchese di Lorne,

3
Abbiamo riscontrato questa eccezione durante l'interruzione di una richiesta Ajax nel browser (utilizzo XMLHttpRequest.abort()).
obecker

2
Una possibile causa potrebbe essere un proxy o un server Http che chiude la connessione troppo presto. Ad esempio un server Apache Http tra il server J2EE e i client dell'applicazione, con un timeout definito breve. Almeno questo è quello che è successo nel nostro ambiente di produzione. I clienti lamentano che le pagine non sono state caricate completamente e abbiamo riscontrato questi errori nel registro JBoss. Dopo alcuni test notiamo che il problema era un server HTTP mal configurato.
Ricardo Vila,

32

Nel nostro caso lo abbiamo riscontrato durante l'esecuzione di un test di carico sul nostro server delle app. Il problema si è rivelato che dobbiamo aggiungere ulteriore memoria alla nostra JVM perché si stava esaurendo. Questo ha risolto il problema.

Prova ad aumentare la memoria disponibile per JVM e monitora l'utilizzo della memoria quando ricevi questi errori.


4
Ho avuto lo stesso problema durante un test di carico. Suppongo che i dati richiedano molto tempo per essere generati e lo strumento di test del carico non attende i dati abbastanza a lungo quindi chiude la connessione. Nel tuo caso, l'aggiunta di memoria potrebbe aver ridotto la durata del processo di generazione dei dati, quindi il test di caricamento ha ottenuto tutti i dati senza il limite di timeout. Un'alternativa per aumentare la memoria sarebbe stata quella di aumentare il timeout dello strumento di test del carico.
Julien Kronegg,

2
Chiaramente la memoria insufficiente ha fatto sì che l'applicazione chiudesse il socket di ricezione o addirittura uscisse del tutto, con lo stesso effetto. La memoria insufficiente da sola non causa tubi rotti.
Marchese di Lorne,

1
questo in realtà sembra essere un problema anche durante la nostra applicazione per carichi pesanti
Ruben de Vries,

7

SocketException: pipe interrotta, è causata dalla "altra estremità" (il client o il server) che chiude la connessione mentre il codice legge o scrive sulla connessione.

Questa è un'eccezione molto comune nelle applicazioni client / server che ricevono traffico da client o server al di fuori del controllo dell'applicazione. Ad esempio, il client è un browser. Se il browser effettua una chiamata Ajax e / o l'utente chiude semplicemente la pagina o il browser, ciò può effettivamente interrompere tutte le comunicazioni in modo imprevisto. Fondamentalmente, vedrai questo errore ogni volta che l'altra estremità termina la loro applicazione e non lo stavi anticipando.

Se si verifica questa eccezione nella propria applicazione, significa che è necessario controllare il codice in cui si verifica l'IO (Input / Output) e racchiuderlo con un blocco try / catch per rilevare questa IOException. Spetta quindi a te decidere come gestire questa situazione semi-valida.

Nel tuo caso, il primo posto in cui hai ancora il controllo è la chiamata a HttpMethodDirector.executeWithRetry- Quindi assicurati che la chiamata sia racchiusa nel blocco try / catch e gestisci come ritieni opportuno.

Consiglio vivamente di non registrare errori specifici di SocketException-Broken Pipe se non a livelli di debug / trace. Altrimenti, questo può essere usato come una forma di attacco DOS (Denial Of Service) compilando i registri. Prova a rafforzare e testare negativamente la tua applicazione per questo scenario comune.


Non è causato dal peer che chiude la connessione mentre stai leggendo da essa. Ciò provoca una condizione diversa, fine del flusso, che spesso non costituisce affatto un'eccezione.
Marchese di Lorne,

5

Tutti i flussi e le connessioni aperti devono essere chiusi correttamente, quindi la prossima volta che proviamo a utilizzare l'oggetto urlConnection, non viene generato un errore. Ad esempio, la seguente modifica del codice ha risolto l'errore per me.

Prima:

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

Dopo:

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
Questo è in realtà molto strano, perché a BufferedOutputStreamchiama sempre close()il suo flusso di output sottostante (quindi sul flusso di output di urlConnection). Vedi docs.oracle.com/javase/6/docs/api/java/io/… e docs.oracle.com/javase/6/docs/api/java/io/… Quindi, quando chiami os.close(), avrebbe dovuto essere già chiuso . No?
obecker

4
'os.close ()' non è 'un must'. Né è 'out.close ()' per quella materia. 'bw.close ()' è sufficiente. @obedker è corretto. Questa modifica da sola non può aver risolto il problema.
Marchese di Lorne,

1

Ho implementato la funzionalità di download dei dati tramite il server FTP e ho trovato la stessa eccezione anche lì riprendendo quel download. Per risolvere questa eccezione, dovrai sempre disconnetterti dalla sessione precedente e creare una nuova istanza del client e una nuova connessione con il server. Questo stesso approccio potrebbe essere utile anche per HTTPClient.


Per risolvere questo errore devi evitare di provare a usare socket chiusi.
Marchese di Lorne,

3
LOL. Potresti perdere tutto il giorno correggendo tutti i consigli fasulli su SO.
Dave,

2
@Dave Infatti. Qualche volta lo faccio.
Marchese di Lorne,

1

Avrei lo stesso problema mentre stavo sviluppando una semplice applicazione Java in ascolto su un TCP specifico. Di solito, non ho avuto problemi, ma quando eseguo alcuni stress test ho notato che alcune connessioni si sono interrotte con errori socket write exception.

Dopo l'indagine ho trovato una soluzione che risolve il mio problema. So che questa domanda è piuttosto vecchia, ma preferisco condividere la mia soluzione, qualcuno può trovarla utile.

Il problema riguardava la creazione di ServerSocket. Ho letto da Javadoc che esiste un limite predefinito di 50 socket in sospeso. Se si tenta di aprire un'altra connessione, queste verranno rifiutate. La soluzione consiste semplicemente nel modificare questa configurazione predefinita sul lato server. Nel caso seguente, creo un server Socket che ascolta sulla porta TCP 10_000e accetto i 200socket in sospeso massimo .

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

Da Javadoc di ServerSocket :

La lunghezza massima della coda per le indicazioni di connessione in entrata (una richiesta di connessione) è impostata sul parametro backlog. Se arriva un'indicazione di connessione quando la coda è piena, la connessione viene rifiutata.


0

Il problema potrebbe essere che i file distribuiti non vengono aggiornati con i metodi RMI corretti. Verifica che l'interfaccia RMI abbia parametri aggiornati o strutture dati aggiornate che il tuo client non ha. O che il tuo client RMI non ha parametri che differiscono da quello della versione del tuo server.

Questa è solo un'ipotesi colta. Dopo aver ridistribuito i file di classe della mia applicazione server e riprovato, il problema di "Broken pipe" è scomparso.


Quali metodi RMI? RMI non è menzionato nella domanda.
Marchese di Lorne,

0

Le risposte sopra illustrano il motivo di ciò java.net.SocketException: Broken pipe: l'altra estremità ha chiuso la connessione. Vorrei condividere l'esperienza di quello che è successo quando l'ho incontrato:

  1. nella richiesta di un client, l' Content-Typeintestazione è erroneamente impostata su un valore maggiore rispetto al corpo della richiesta (in realtà non esiste alcun corpo)
  2. il servizio di fondo nel socket Tomcat era in attesa di dati corporei di quelle dimensioni (http è su TCP che garantisce la consegna incapsulando e ...)
  3. alla scadenza di 60 secondi, tomcat genera un'eccezione di timeout: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception java.net.SocketTimeoutException: null
  4. il client riceve una risposta con il codice di stato 500 a causa dell'eccezione di timeout.
  5. connessione stretta client (perché riceve risposta).
  6. tomcat lancia java.net.SocketException: Broken pipeperché il client lo ha chiuso.

A volte, tomcat non genera un'eccezione pip interrotta, perché l'eccezione di timeout chiude la connessione, perché anche questa differenza mi confonde.


L' Content-Typeintestazione non specifica un numero, quindi non può essere "più grande" di qualsiasi altra cosa. Le eccezioni di timeout non chiudono il socket. La risposta non ha alcun senso.
Marchese di Lorne,

@EJP forse era la lunghezza dei contenuti, non ricordo. Penso che il timeout in Tomcat ritorni a 500, mentre il client riceve questa risposta e chiude la connessione, il che alla fine fa sì che Tomcat non riceva abbastanza byte come previsto.
Tiina,

Se Tomcat invia 500, ha già ricevuto tutto ciò che prevede. Non ha ancora senso.
Marchese di Lorne,

@ user207421 non proprio. Tomcat invia 500 perché non riceve tutto ciò che si aspetta, ma è scaduto.
Tiina,

-1

JavaDoc:

La lunghezza massima della coda per le indicazioni di connessione in entrata (una richiesta di connessione) è impostata su 50. Se arriva un'indicazione di connessione quando la coda è piena, la connessione viene rifiutata.

Ad esempio, è necessario aumentare il parametro "backlog" di ServerSocket

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

1
L'aumento del backlog non risolverà un errore "pipe spezzata".
Marchese di Lorne,

1
ma mi ha aiutato
secondo

@EJP Ho testato un server su Linux e ha funzionato, ma su Mac OS - no. E l'aumento delle dimensioni del backlog mi ha aiutato. Non sapevo che la dimensione massima fosse 50.
Secondo

1
Hai provato qualcos'altro. Ascolta il backlog non ha nulla a che fare con questa eccezione. Aiuta a rifiutare connessioni o timeout al client. Non eccezioni "socket chiuso", che sono un errore locale.
Marchese di Lorne,

Per i pool di socket (come un server HTTP come Tomcat) ci sono impostazioni di configurazione per il numero di "accetta" in sospeso che il server "metterà in coda" prima di inviare i thread di manutenzione e un'impostazione separata per il numero di thread nel pool. Tuttavia, nulla di tutto ciò è correlato al problema che dopo che il server "accetta ()" quando viene stabilita la connessione - il flusso di output viene improvvisamente (inconsapevolmente) "chiuso".
Darrell Teague,

-1

La causa è che il peer remoto chiude il suo socket (crash ad esempio), quindi se provi a scrivergli dei dati, ad esempio, otterrai questo. per risolvere questo problema, proteggere le operazioni di lettura / scrittura tra socket (provare a catturare), quindi gestire il caso di perdita della connessione nella cattura (rinnovare la connessione, interrompere il programma, ecc.):

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

3
Ciò registrerà i problemi, ma non li risolverà . Per risolverli è necessario (a) risolvere eventuali errori del protocollo dell'applicazione e (b) chiudere le connessioni morte. Non solo le eccezioni del registro, molte delle quali sono fatali.
Marchese di Lorne,

@EJP: ovviamente, quando si rileva un errore, è necessario pulire il socket (contesto) nell'istruzione catch. Non riesco a indovinare cosa farà lo sviluppatore. è suo
compito

Spetta allo sviluppatore correggere il problema del protocollo dell'applicazione che causa l'errore, e c'è. Niente che la tua risposta o codice che lo aiuti a. fare così. 'Inserisci operazioni di lettura / scrittura tra' non risolve. La tua risposta è errata
Marchese di Lorne,

@ user207421, lo sviluppatore chiede come riparare il tubo rotto. gli ho indicato di proteggere innanzitutto il suo programma usando (prova a catturare). questo eviterà l'arresto anomalo del programma. quindi inserisco un commento per indicargli di eseguire un'elaborazione pulita. Generalmente in caso di errori nella pipe, ci sono molte alternative, non riesco a indovinare l'obiettivo del programma, interrompere il programma, rinnovare la connessione, i dati sono danneggiati o no .... ecc. spetta al programmatore decidere quale opzione fare? cosa c'era di sbagliato nella mia risposta?
elhadi dp ıpɐɥ ן ǝ
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.