Como corrigir java.net.SocketException: Broken pipe?


150

Estou usando o cliente http apache commons para chamar url usando o método post para postar os parâmetros e está lançando o erro abaixo raramente.

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)

Alguém pode sugerir o que está causando essa exceção e como depurá-la?


Respostas:


81

Isso é causado por:

  • geralmente, gravando em uma conexão quando a outra extremidade já a fechou;
  • menos normalmente, o par fecha a conexão sem ler todos os dados que já estão pendentes no final.

Portanto, nos dois casos, você possui um protocolo de aplicativo mal definido ou implementado.

Há uma terceira razão que não documentarei aqui, mas que envolve o ponto de executar ações deliberadas para redefinir em vez de fechar adequadamente a conexão.


4
Você pode me ajudar a identificá-lo e corrigi-lo? Basicamente, como confirmar o motivo e corrigir?

4
Eu confirmou a razão. Você está escrevendo enquanto a outra extremidade já fechou a conexão. A correção é não fazer isso. Como o outro lado não está lendo, não há sentido em tudo. Como eu também disse, se isso estiver acontecendo, há algo errado com a especificação ou implementação do protocolo de aplicativo, provavelmente você nem sequer tem um.
Marquês de Lorne

26
Se o aplicativo HTTP do lado do servidor estiver recebendo exceções do Broken Pipe, isso significa apenas que o navegador do cliente saiu / foi para outra página / atingiu o tempo limite / voltou no histórico / o que seja. Apenas esqueça.
Marquês de Lorne

3
Vimos essa exceção ao interromper uma solicitação de ajax no navegador (usando XMLHttpRequest.abort()).
obecker

2
Uma causa possível seria um servidor proxy ou HTTP fechando a conexão muito cedo. Por exemplo, um servidor Apache Http entre o servidor J2EE e os clientes de aplicativos, com um tempo limite curto definido. Pelo menos foi o que aconteceu no nosso ambiente de produção. Reclamação de clientes sobre as páginas não estarem totalmente carregadas e vimos esses erros no log do JBoss. Após alguns testes, percebemos que o problema era um servidor HTTP mal configurado.
Ricardo Vila

32

No nosso caso, experimentamos isso ao executar um teste de carga em nosso servidor de aplicativos. Ocorreu o problema de que precisamos adicionar memória adicional à nossa JVM porque ela estava acabando. Isso resolveu o problema.

Tente aumentar a memória disponível para a JVM e ou monitore o uso de memória quando receber esses erros.


4
Eu tive o mesmo problema durante um teste de carga. Eu acho que os dados levam muito tempo para serem gerados e a ferramenta de teste de carga não espera os dados por tempo suficiente e fecha a conexão. No seu caso, adicionar memória pode ter reduzido a duração do processo de geração de dados, para que o teste de carga obtenha todos os dados sem o limite de tempo limite. Uma alternativa para aumentar a memória seria aumentar o tempo limite da ferramenta de teste de carga.
Julien Kronegg

2
Claramente, a pouca memória fez com que o aplicativo fechasse o soquete de recebimento ou saia completamente, com o mesmo efeito. A baixa memória por si só não causa canos quebrados.
Marquês de Lorne

1
isso realmente parece ser um problema durante a aplicação de carga pesada, bem
Ruben de Vries

7

SocketException: canal quebrado, é causado pelo 'outro lado' (o cliente ou o servidor) que fecha a conexão enquanto o seu código está lendo ou gravando na conexão.

Essa é uma exceção muito comum nos aplicativos cliente / servidor que recebem tráfego de clientes ou servidores fora do controle do aplicativo. Por exemplo, o cliente é um navegador. Se o navegador fizer uma chamada Ajax e / ou o usuário simplesmente fechar a página ou o navegador, isso poderá efetivamente eliminar toda a comunicação inesperadamente. Basicamente, você verá esse erro sempre que a outra extremidade finalizar o aplicativo e você não o esperava.

Se você tiver essa exceção no seu aplicativo, significa que deve verificar seu código onde a IO (entrada / saída) ocorre e envolvê-la com um bloco try / catch para capturar essa IOException. Cabe a você decidir como deseja lidar com essa situação semi-válida.

No seu caso, o primeiro local em que você ainda tem controle é a chamada para HttpMethodDirector.executeWithRetry- Portanto, assegure-se de que a chamada seja encerrada com o bloco try / catch e trate-a como achar melhor.

Eu recomendaria fortemente contra o registro de erros específicos de SocketException-Broken Pipe em algo diferente de níveis de depuração / rastreamento. Senão, isso pode ser usado como uma forma de ataque do DOS (Denial Of Service), preenchendo os logs. Tente fortalecer e testar negativamente seu aplicativo para esse cenário comum.


Isso não é causado pelo ponto que fecha a conexão enquanto você a lê. Isso causa uma condição diferente de final do fluxo, que geralmente não é uma exceção.
Marquês de Lorne

5

Todos os fluxos e conexões abertos precisam ser fechados corretamente, portanto, da próxima vez que tentarmos usar o objeto urlConnection, ele não emitirá um erro. Como exemplo, a seguinte alteração de código corrigiu o erro para mim.

Antes:

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

Depois de:

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
Isso é realmente muito estranho, porque um BufferedOutputStreamsempre chama close()seu fluxo de saída subjacente (portanto, o fluxo de saída de urlConnection). Consulte docs.oracle.com/javase/6/docs/api/java/io/… e docs.oracle.com/javase/6/docs/api/java/io/… Portanto, quando você ligar, os.close()já deve ter sido fechado . Não?
obecker

4
'os.close ()' não é 'obrigatório'. Nem é 'out.close ()' para esse assunto. 'bw.close ()' é suficiente. @obedker está correto. Essa alteração sozinha não pode ter resolvido o problema.
Marquês de Lorne

1

Eu implementei a funcionalidade de download de dados através do servidor FTP e também encontrei a mesma exceção ao continuar o download. Para resolver essa exceção, você sempre precisará se desconectar da sessão anterior e criar uma nova instância do cliente e uma nova conexão com o servidor. Essa mesma abordagem também pode ser útil para o HTTPClient.


Para resolver esse erro, evite tentar usar soquetes fechados.
Marquês de Lorne #

3
RI MUITO. Você poderia perder o dia todo corrigindo todos os conselhos falsos sobre SO.
19716 Dave

2
@Dave De fato. Ás vezes eu faço.
Marquês de Lorne

1

Eu tinha o mesmo problema enquanto desenvolvia um aplicativo Java simples que escuta um TCP específico. Normalmente, não tinha nenhum problema, mas quando executo algum teste de estresse, notei que alguma conexão quebrou com erro socket write exception.

Após a investigação, encontrei uma solução que resolve meu problema. Sei que essa pergunta é bastante antiga, mas prefiro compartilhar minha solução, alguém pode achar útil.

O problema estava na criação do ServerSocket. Eu li no Javadoc, há um limite padrão de 50 soquetes pendentes. Se você tentar abrir outra conexão, elas serão recusadas. A solução consiste simplesmente em alterar essa configuração padrão no lado do servidor. No caso a seguir, crio um servidor de soquete que escuta na porta TCP 10_000e aceita o máximo de 200soquetes pendentes.

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

Do Javadoc do ServerSocket :

O comprimento máximo da fila para indicações de conexão de entrada (uma solicitação para conectar) é definido como o parâmetro backlog. Se uma indicação de conexão chegar quando a fila estiver cheia, a conexão será recusada.


0

O problema pode ser que seus arquivos implantados não são atualizados com os métodos RMI corretos. Verifique se sua interface RMI possui parâmetros atualizados ou estruturas de dados atualizadas que seu cliente não possui. Ou que seu cliente RMI não possui parâmetros diferentes da versão do servidor.

Este é apenas um palpite. Depois de reimplantar os arquivos de classe do meu aplicativo de servidor e testar novamente, o problema do "Tubo quebrado" desapareceu.


Quais métodos de RMI? O RMI não é mencionado na pergunta.
Marquês de Lorne

0

As respostas acima ilustram o motivo disso java.net.SocketException: Broken pipe: a outra extremidade fechou a conexão. Gostaria de compartilhar a experiência do que aconteceu quando o encontrei:

  1. na solicitação de um cliente, o Content-Typecabeçalho é definido erroneamente como maior que o corpo da solicitação (na verdade, não havia nenhum corpo)
  2. o serviço inferior no soquete do tomcat estava aguardando os dados do corpo dimensionado (http está no TCP, o que garante a entrega encapsulando e ...)
  3. quando 60 segundos expiram, o tomcat lança a exceção de tempo limite: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception java.net.SocketTimeoutException: null
  4. o cliente recebe uma resposta com o código de status 500 devido à exceção de tempo limite.
  5. conexão de fechamento do cliente (porque recebe resposta).
  6. o tomcat lança java.net.SocketException: Broken pipeporque o cliente fechou.

Às vezes, o tomcat não lança uma exceção de pip quebrada, porque a exceção de tempo limite fecha a conexão, por que essa diferença está me confundindo também.


O Content-Typecabeçalho não especifica um número, portanto, não pode ser 'maior' do que qualquer coisa. Exceções de tempo limite não fecham o soquete. A resposta não faz nenhum sentido.
Marquês de Lorne

@EJP talvez tenha o tamanho do conteúdo, não se lembra. Eu acho que o tempo limite no Tomcat faz com que ele retorne 500, enquanto o cliente recebe essa resposta e fecha a conexão, o que finalmente faz com que o tomcat não receba bytes suficientes conforme o esperado.
Tiina

Se o Tomcat envia 500, ele já recebeu tudo o que planeja. Ainda não faz sentido.
Marquês de Lorne

@ user207421 não é verdade. O Tomcat envia 500 porque não recebe tudo o que espera, mas atingiu o tempo limite.
Tiina

-1

JavaDoc:

O comprimento máximo da fila para indicações de conexão de entrada (uma solicitação para conectar) é definido como 50. Se uma indicação de conexão chegar quando a fila estiver cheia, a conexão será recusada.

Você deve aumentar o parâmetro "backlog" do seu ServerSocket, por exemplo

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

1
Aumentar a lista de pendências não solucionará um erro de "tubo quebrado".
Marquês de Lorne

1
mas ajudou-me
TheSecond

@EJP Testei um servidor no Linux e funcionou, mas no Mac OS - não. E aumentar o tamanho da lista de pendências me ajudou. Eu não sabia que o tamanho máximo é 50.
TheSecond 25/02

1
Você testou outra coisa. O backlog de escuta não tem nada a ver com essa exceção. Ajuda recusas ou tempos limite de conexão no cliente. Não são exceções de 'soquete fechado', que são um erro local.
Marquês de Lorne

Para conjuntos de soquetes (como um servidor HTTP como o Tomcat), existem definições de configuração para o número de "aceites" pendentes, o servidor "enfileirará" antes de despachar threads de manutenção e uma configuração separada para o número de threads no pool. No entanto, nada disso está relacionado ao problema que após o servidor "aceitar ()" quando a conexão é estabelecida - o fluxo de saída é repentinamente (inconscientemente) "fechado".
Darrell Teague

-1

A causa é que o ponto remoto fecha seu soquete (falha por exemplo); portanto, se você tentar gravar dados nele, por exemplo, você obterá isso. para resolver isso, proteja as operações de leitura / gravação no soquete entre (tente pegar) e gerencie o caso de perda de conexão no problema (renove a conexão, pare o programa, etc. etc):

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

3
Isso registrará os problemas, mas não os resolverá . Para resolvê- los, você deve (a) resolver possíveis erros do protocolo de aplicativos e (b) fechar as conexões mortas. Não apenas exceções de log, a maioria das quais é fatal.
Marquês de Lorne

@EJP: é claro que quando você pegar um erro, limpe seu soquete (contexto) na instrução catch. Não consigo adivinhar o que o desenvolvedor fará. é no seu próprio para fazer o procedimento limpa
elhadi dp ıpɐɥ ן ǝ

Cabe ao desenvolvedor corrigir o problema do protocolo de aplicativo que causa o erro, e existe. Nada da sua resposta ou código que o ajude. faça isso. 'Inserir operações de leitura / gravação entre' não o corrige. Sua resposta está incorreta.
Marquês de Lorne

@ user207421, o desenvolvedor pergunta como consertar o tubo quebrado. Eu indiquei a ele para proteger primeiro seu programa usando (tente pegar). isso evitará que o programa falhe. então insiro um comentário para indicar a ele para fazer um processamento limpo. Geralmente em erros de tubo quebrado, existem muitas alternativas, não consigo adivinhar o objetivo do programa, interromper o programa, renovar a conexão, os dados estão corrompidos ou não .... etc. cabe ao programador decidir qual opção fazer? o que havia de errado com a minha resposta?
elhadi dp ıpɐɥ ן ǝ 27/01/19
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.