¿Cómo arreglar java.net.SocketException: tubería rota?

150

Estoy usando el cliente http apache commons para llamar a la URL usando el método de publicación para publicar los parámetros y rara vez arroja el siguiente error.

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)

¿Alguien puede sugerir qué está causando esta excepción y cómo depurarla?

Eric Leschinski
fuente
1
es mejor probar stackoverflow.com/questions/10142409/…
Vimalkumar Natarajan

Respuestas:

81

Esto es causado por:

  • generalmente, escribir en una conexión cuando el otro extremo ya la ha cerrado;
  • menos habitualmente, el par cierra la conexión sin leer todos los datos que ya están pendientes al final.

Entonces, en ambos casos, tiene un protocolo de aplicación mal definido o implementado.

Hay una tercera razón que no documentaré aquí, pero que implica que los pares tomen medidas deliberadas para restablecer en lugar de cerrar correctamente la conexión.

Marqués de Lorne
fuente
44
¿Pueden ayudarme a identificarlo y corregirlo? Básicamente, ¿cómo confirmar el motivo y corregirlo?
44
Yo he confirmado la razón. Está escribiendo mientras el otro extremo ya ha cerrado la conexión. La solución es no hacer eso. Como el otro extremo no lo está leyendo, de todos modos no tiene sentido. Como también dije, si esto está sucediendo, hay algo mal con la especificación o implementación del protocolo de su aplicación, lo más probable es que ni siquiera tenga uno.
Marqués de Lorne
26
Si la aplicación HTTP del lado del servidor está recibiendo excepciones de Broken Pipe, solo significa que el navegador del cliente ha salido / ido a otra página / agotó el tiempo de espera / regresó al historial / lo que sea. Solo olvídalo.
Marqués de Lorne
3
Hemos visto esta excepción al cancelar una solicitud ajax en el navegador (usando XMLHttpRequest.abort()).
obecker 01 de
2
Una posible causa sería un servidor proxy o Http que cierre la conexión demasiado pronto. Por ejemplo, un servidor Apache Http entre su servidor J2EE y los clientes de la aplicación, con un breve tiempo de espera definido. Al menos eso fue lo que sucedió en nuestro entorno de producción. Los clientes se quejan de que las páginas no están completamente cargadas y vimos estos errores en el registro de JBoss. Después de algunas pruebas, notamos que el problema era un servidor Http mal configurado.
Ricardo Vila
32

En nuestro caso, experimentamos esto al realizar una prueba de carga en nuestro servidor de aplicaciones. El problema resultó que necesitamos agregar memoria adicional a nuestra JVM porque se estaba agotando. Esto resolvió el problema.

Intente aumentar la memoria disponible para la JVM o supervise el uso de la memoria cuando reciba esos errores.

Dave
fuente
44
Tengo el mismo problema durante una prueba de carga. Supongo que los datos tardan mucho tiempo en generarse y la herramienta de prueba de carga no espera los datos el tiempo suficiente y luego cierra la conexión. En su caso, agregar memoria puede haber reducido la duración del proceso de generación de datos, por lo que la prueba de carga obtuvo todos los datos sin el límite de tiempo de espera. Una alternativa para aumentar la memoria habría sido aumentar el tiempo de espera de la herramienta de prueba de carga.
Julien Kronegg el
2
Claramente, la poca memoria provocó que la aplicación cerrara el socket de recepción o, de hecho, saliera por completo, con el mismo efecto. La poca memoria por sí sola no causa tuberías rotas.
Marqués de Lorne
1
esto realmente parece ser un problema durante nuestra aplicación de carga pesada también
Ruben de Vries
7

SocketException: tubería rota, es causada por el 'otro extremo' (el cliente o el servidor) que cierra la conexión mientras su código lee o escribe en la conexión.

Esta es una excepción muy común en aplicaciones cliente / servidor que reciben tráfico de clientes o servidores fuera del control de la aplicación. Por ejemplo, el cliente es un navegador. Si el navegador realiza una llamada Ajax, y / o el usuario simplemente cierra la página o el navegador, esto puede matar efectivamente toda comunicación inesperadamente. Básicamente, verá este error cada vez que el otro extremo finalice su aplicación, y no lo estaba anticipando.

Si experimenta esta excepción en su aplicación, significa que debe verificar su código donde ocurre el IO (entrada / salida) y envolverlo con un bloque try / catch para capturar esta IOException. Entonces depende de usted decidir cómo quiere manejar esta situación semi-válida.

En su caso, el primer lugar donde aún tiene control es la llamada a HttpMethodDirector.executeWithRetry- Así que asegúrese de que la llamada esté envuelta con el bloque try / catch y manejela como mejor le parezca.

Recomiendo encarecidamente que no se registren errores específicos de SocketException-Broken Pipe en cualquier otra cosa que no sean los niveles de depuración / rastreo. De lo contrario, esto se puede usar como una forma de ataque DOS (Denegación de servicio) al llenar los registros. Intente y endurezca y pruebe negativamente su aplicación para este escenario común.

AndyGee
fuente
No es causado por el par que cierra la conexión mientras está leyendo. Eso causa una condición diferente de final de flujo, que a menudo no es una excepción en absoluto.
Marqués de Lorne
5

Todas las secuencias y conexiones abiertas deben cerrarse correctamente, por lo que la próxima vez que intentemos usar el objeto urlConnection, no arrojará un error. Como ejemplo, el siguiente cambio de código solucionó el error para mí.

Antes de:

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

Después:

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.
Murali Mohan
fuente
8
Esto es realmente muy extraño, porque un BufferedOutputStreamsiempre llama close()a su flujo de salida subyacente (por lo tanto, al flujo de salida de urlConnection). Ver docs.oracle.com/javase/6/docs/api/java/io/… y docs.oracle.com/javase/6/docs/api/java/io/… Entonces, cuando llame, os.close()ya debería estar cerrado . ¿No?
obecker 01 de
44
'os.close ()' no es 'imprescindible'. Tampoco es 'out.close ()' para el caso. 'bw.close ()' es suficiente. @obedker es correcto. Este cambio por sí solo no puede haber solucionado el problema.
Marqués de Lorne
1

Implementé la funcionalidad de descarga de datos a través del servidor FTP y encontré la misma excepción allí al reanudar esa descarga. Para resolver esta excepción, siempre tendrá que desconectarse de la sesión anterior y crear una nueva instancia del Cliente y una nueva conexión con el servidor. Este mismo enfoque también podría ser útil para HTTPClient.

Naeem Ahmad
fuente
Para resolver este error, debe evitar intentar usar sockets cerrados.
Marqués de Lorne
3
Jajaja Podría perder todo el día corrigiendo todos los consejos falsos sobre SO.
Dave
2
@Dave hecho. A veces lo hago.
Marqués de Lorne
1

Tuve el mismo problema mientras desarrollaba una aplicación Java simple que escucha en un TCP específico. Por lo general, no tuve ningún problema, pero cuando ejecuté una prueba de esfuerzo, noté que algunas conexiones se rompieron con un error socket write exception.

Después de la investigación, encontré una solución que resuelve mi problema. Sé que esta pregunta es bastante antigua, pero prefiero compartir mi solución, alguien puede encontrarla útil.

El problema estaba en la creación de ServerSocket. Leí de Javadoc que hay un límite predeterminado de 50 sockets pendientes. Si intenta abrir otra conexión, se rechazarán. La solución consiste simplemente en cambiar esta configuración predeterminada en el lado del servidor. En el siguiente caso, creo un servidor de Socket que escucha en el puerto TCP 10_000y acepta max 200sockets pendientes.

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

Desde Javadoc de ServerSocket :

La longitud máxima de la cola para las indicaciones de conexión entrante (una solicitud de conexión) se establece en el parámetro de acumulación. Si llega una indicación de conexión cuando la cola está llena, se rechaza la conexión.

xcesco
fuente
0

El problema podría ser que sus archivos desplegados no se actualicen con los métodos RMI correctos. Verifique que su interfaz RMI tenga parámetros actualizados o estructuras de datos actualizadas que su cliente no tiene. O que su cliente RMI no tiene parámetros que difieran de la versión de su servidor.

Esto es solo una suposición educada. Después de volver a implementar los archivos de clase de mi aplicación de servidor y volver a realizar las pruebas, el problema de "tubería rota" desapareció.

JonAar Livernois
fuente
¿Qué métodos de RMI? RMI no se menciona en la pregunta.
Marqués de Lorne
0

Las respuestas anteriores ilustran la razón de esto java.net.SocketException: Broken pipe: el otro extremo cerró la conexión. Me gustaría compartir la experiencia de lo que sucedió cuando lo encontré:

  1. en la solicitud de un cliente, el Content-Typeencabezado se establece erróneamente más grande que el cuerpo de la solicitud (de hecho, no había ningún cuerpo)
  2. el servicio inferior en el socket tomcat estaba esperando los datos de ese tamaño del cuerpo (http está en TCP que asegura la entrega encapsulando y ...)
  3. Cuando caducaron 60 segundos, Tomcat lanza una excepción de tiempo de espera: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception java.net.SocketTimeoutException: null
  4. el cliente recibe una respuesta con el código de estado 500 debido a la excepción de tiempo de espera.
  5. conexión cercana del cliente (porque recibe respuesta).
  6. tomcat lanza java.net.SocketException: Broken pipeporque el cliente lo cerró.

A veces, tomcat no arroja una excepción de pip roto, porque la excepción de tiempo de espera cierra la conexión, por qué esa diferencia también me confunde.

Tiina
fuente
El Content-Typeencabezado no especifica un número, por lo que no puede ser "más grande" que nada. Las excepciones de tiempo de espera no cierran el socket. La respuesta no tiene ningún sentido.
Marqués de Lorne
@EJP tal vez fue la longitud del contenido, no lo recuerdo. Creo que el tiempo de espera en Tomcat hace que devuelva 500, mientras que el cliente recibe esta respuesta y cierra la conexión, lo que finalmente hace que tomcat no reciba suficientes bytes como se espera.
Tiina
Si Tomcat envía 500, ya ha recibido todo lo que planea. Aún no tiene sentido.
Marqués de Lorne
@ user207421 no realmente. Tomcat envía 500 porque no recibe todo lo que espera, pero se agotó el tiempo de espera.
Tiina
-1

JavaDoc:

La longitud máxima de la cola para las indicaciones de conexión entrante (una solicitud de conexión) se establece en 50. Si llega una indicación de conexión cuando la cola está llena, se rechaza la conexión.

Debe aumentar el parámetro "backlog" de su ServerSocket, por ejemplo

int backlogSize = 50 ;
new ServerSocket(port, backlogSize);
El segundo
fuente
1
El aumento de la acumulación no resolverá un error de 'tubería rota'.
Marqués de Lorne
1
pero me ayudó
TheSecond
@EJP Probé un servidor en Linux y funcionó, pero en Mac OS, no. Y el aumento del tamaño de la reserva me ayudó. No sabía que el tamaño máximo es 50.
segundo
1
Probaste algo más. Listen backlog no tiene nada que ver con esta excepción. Ayuda a rechazar conexiones o tiempos de espera en el cliente. No hay excepciones de "socket cerrado", que son un error local.
Marqués de Lorne
Para los grupos de sockets (como un servidor HTTP como Tomcat) hay opciones de configuración para el número de "acepta" pendientes que el servidor "pondrá en cola" antes de enviar subprocesos de servicio y una configuración separada para el número de subprocesos en el grupo. Sin embargo, nada de esto está relacionado con el problema de que después de que el Servidor "acepte ()" cuando se establece la conexión, la secuencia de salida se "cierra" repentinamente (sin darse cuenta).
Darrell Teague el
-1

La causa es que el par remoto cierra su socket (bloqueo, por ejemplo), por lo que si intenta escribir datos en él, por ejemplo, obtendrá esto. para resolver eso, proteja las operaciones de lectura / escritura en el socket entre (intente catch), luego administre el caso de pérdida de conexión en el catch (renueve la conexión, pare el programa, ... etc.):

 try {
      /* your code */
 } catch (SocketException e) {
      e.printStackTrace();
      /* insert your failure processings here */
 }
elhadi dp ıpɐɥ ן ǝ
fuente
3
Esto registrará los problemas, pero no los resolverá . Para resolverlos , debe (a) resolver los posibles errores de protocolo de aplicación y (b) cerrar las conexiones inactivas. No solo las excepciones de registro, la mayoría de las cuales son fatales.
Marqués de Lorne
@EJP: por supuesto, cuando detecta un error, debe limpiar su socket (contexto) en la instrucción catch. No puedo adivinar qué va a hacer el desarrollador. es él quien debe realizar el procedimiento de limpieza
elhadi dp ıpɐɥ ן ǝ
Depende del desarrollador corregir el problema del protocolo de aplicación que causa el error, y lo hay. Nada de su respuesta o código que lo ayude. hazlo 'Insertar operaciones de lectura / escritura entre' no lo arregla. Tu respuesta es incorrecta.
Marqués de Lorne
@ user207421, el desarrollador pregunta cómo arreglar la tubería rota. Le he indicado que proteja en primer lugar su programa mediante el uso de (try catch). Esto evitará que el programa se bloquee. luego inserto un comentario para indicarle que realice un procesamiento limpio. En general, en un error de tubería rota, hay muchas alternativas, no puedo adivinar el objetivo del programa, detener el programa, renovar la conexión, los datos están dañados o no ... etc. depende del programador decidir qué opción hacer? ¿Qué estaba mal con mi respuesta?
elhadi dp ıpɐɥ ן ǝ