Как исправить java.net.SocketException: сломанная труба?


150

Я использую Apache http клиента клиента для вызова URL, используя метод post для публикации параметров, и редко выдает ошибку ниже.

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)

Может кто-нибудь подсказать, что вызывает это исключение и как его отладить?


Ответы:


81

Это вызвано:

  • чаще всего, запись в соединение, когда другой конец уже закрыл его;
  • реже, одноранговый узел закрывает соединение, не считывая все данные, которые уже ожидают в его конце.

Таким образом, в обоих случаях у вас есть плохо определенный или реализованный протокол приложения.

Есть третья причина, которую я не буду здесь документировать, но которая заключается в том, что одноранговый узел предпринимает преднамеренные действия для сброса, а не для правильного закрытия соединения.


4
Можете ли вы помочь мне определить и исправить это? В основном, как подтвердить причину и исправить?

4
Я уже подтвердил причину. Вы пишете, в то время как другой конец уже закрыл соединение. Исправление не состоит в том, чтобы сделать это. Поскольку другой конец не читает, нет никакого смысла в любом случае. Как я уже сказал, если это происходит, что-то не так с вашей спецификацией или реализацией протокола приложения, скорее всего, у вас его даже нет.
маркиз Лорн

26
Если серверное HTTP-приложение получает исключения Broken Pipe, это просто означает, что клиентский браузер завершил работу / перешел на другую страницу / истек тайм-аут / вернулся в историю / что угодно. Просто забудь об этом.
маркиз Лорн

3
Мы видели это исключение при прерывании ajax-запроса в браузере (используя XMLHttpRequest.abort()).
obecker

2
Одной из возможных причин может быть прокси или Http-сервер, слишком рано закрывающий соединение. Например, сервер Apache Http между вашим сервером J2EE и клиентами приложений с коротким заданным временем ожидания. По крайней мере, так было в нашей производственной среде. Клиенты жалуются на то, что страницы загружаются не полностью, и мы увидели эти ошибки в журнале JBoss. После некоторого теста мы заметили, что проблема была в плохо настроенном Http-сервере.
Рикардо Вила

32

В нашем случае мы столкнулись с этим при выполнении нагрузочного теста на нашем сервере приложений. Выяснилось, что нам нужно добавить дополнительную память в нашу JVM, потому что она заканчивалась. Это решило проблему.

Попробуйте увеличить объем памяти, доступной для JVM, и / или отслеживать использование памяти, когда вы получаете эти ошибки.


4
Я получил ту же проблему во время нагрузочного теста. Я полагаю, что генерация данных занимает много времени, и инструмент нагрузочного тестирования не ждет данных достаточно долго, а затем закрывает соединение. В вашем случае добавление памяти могло сократить продолжительность процесса генерации данных, поэтому нагрузочный тест получил все данные без ограничения по времени ожидания. Альтернативой увеличению памяти было бы увеличение времени ожидания инструмента нагрузочного тестирования.
Жюльен Кронегг

2
Очевидно, что из-за нехватки памяти приложение закрывало приемный сокет или вообще завершало работу с тем же эффектом. Недостаток памяти сам по себе не приводит к поломке труб.
Маркиз Лорн

1
на самом деле это, похоже, проблема и для нашего приложения с большой нагрузкой
Рубен де Фриз

7

SocketException: Сломанный канал, вызван тем, что «другой конец» (клиент или сервер) закрывает соединение, пока ваш код либо читает, либо пишет в соединение.

Это очень распространенное исключение в клиент-серверных приложениях, которые получают трафик от клиентов или серверов вне контроля приложений. Например, клиент является браузером. Если браузер выполняет Ajax-вызов и / или пользователь просто закрывает страницу или браузер, то это может неожиданно прекратить все коммуникации. По сути, вы увидите эту ошибку каждый раз, когда другой конец завершает свое приложение, и вы не ожидали этого.

Если вы испытываете это Исключение в своем приложении, то это означает, что вы должны проверить свой код, где происходит IO (Ввод / Вывод), и обернуть его блоком try / catch, чтобы перехватить это IOException. Тогда вам решать, как вы хотите справиться с этой полуактивной ситуацией.

В вашем случае, самое раннее место, где у вас все еще есть контроль, это вызов HttpMethodDirector.executeWithRetry Поэтому убедитесь, что вызов обернут блоком try / catch, и обрабатывайте его так, как считаете нужным.

Я настоятельно рекомендую не регистрировать конкретные ошибки SocketException-Broken Pipe ни на каких других уровнях, кроме уровней отладки / трассировки. Иначе, это может быть использовано как форма атаки DOS (отказ в обслуживании), заполняя логи. Попытайтесь укрепить и негативно протестировать ваше приложение для этого распространенного сценария.


Это не вызвано тем, что узел закрывает соединение, пока вы читаете с него. Это вызывает другое условие окончания потока, которое часто вообще не является исключением.
Маркиз Лорн

5

Все открытые потоки и соединения должны быть правильно закрыты, поэтому в следующий раз, когда мы попытаемся использовать объект urlConnection, он не выдаст ошибку. Как пример, следующее изменение кода исправило ошибку для меня.

Перед:

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

После:

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
Это на самом деле очень странно, потому что a BufferedOutputStreamвсегда вызывает close()свой основной выходной поток (таким образом, выходной поток urlConnection). См. Docs.oracle.com/javase/6/docs/api/java/io/… и docs.oracle.com/javase/6/docs/api/java/io/… Так что, когда вы звоните, os.close()он должен быть уже закрыт , Нет?
obecker

4
os.close () не является обязательным. В этом отношении также не существует out.close (). 'bw.close ()' достаточно. @obedker правильно. Одно только это изменение не могло решить проблему.
маркиз Лорн

1

Я реализовал функцию загрузки данных через FTP-сервер и нашел там то же исключение при возобновлении загрузки. Чтобы устранить это исключение, вам всегда нужно будет отключиться от предыдущего сеанса и создать новый экземпляр клиента и новое соединение с сервером. Этот же подход может быть полезен и для HTTPClient.


Чтобы устранить эту ошибку, вы должны избегать использования закрытых сокетов.
маркиз Лорн

3
РЖУНИМАГУ. Вы можете потратить весь день на исправление всех поддельных советов по SO.
Дейв,

2
@ Дейв В самом деле. Иногда я делаю.
Маркиз Лорн

1

У меня была такая же проблема, когда я разрабатывал простое Java-приложение, которое прослушивает определенный TCP. Обычно у меня не возникало проблем, но при запуске стресс-теста я заметил, что какое-то соединение оборвалось с ошибкойsocket write exception .

После расследования я нашел решение, которое решает мою проблему. Я знаю, что этот вопрос довольно старый, но я предпочитаю поделиться своим решением, кто-то может найти его полезным.

Проблема была в создании ServerSocket. Я читал из Javadoc, есть ограничение по умолчанию в 50 ожидающих сокетов. Если вы попытаетесь открыть другое соединение, вам будет отказано. Решение состоит просто в изменении конфигурации по умолчанию на стороне сервера. В следующем случае я создаю сервер Socket, который прослушивает порт TCP 10_000и принимает максимальное количество 200ожидающих сокетов.

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

Из Javadoc ServerSocket :

Максимальная длина очереди для индикации входящих соединений (запрос на соединение) задается параметром backlog. Если индикация соединения появляется, когда очередь заполнена, соединение отклоняется.


0

Проблема может заключаться в том, что ваши развернутые файлы не обновляются с правильными методами RMI. Убедитесь, что ваш интерфейс RMI имеет обновленные параметры или обновленные структуры данных, которых нет у вашего клиента. Или что ваш клиент RMI не имеет параметров, которые отличаются от того, что имеет версия вашего сервера.

Это просто обоснованное предположение. После повторного развертывания файлов классов моего серверного приложения и повторного тестирования проблема «Сломанной трубы» ушла.


Какие методы RMI? RMI не упоминается в вопросе.
Маркиз Лорн

0

Приведенные выше ответы иллюстрируют причину этого java.net.SocketException: Broken pipe: другой конец закрыл соединение. Я хотел бы поделиться опытом, что случилось, когда я столкнулся с этим:

  1. в запросе клиента Content-Typeзаголовок ошибочно установлен больше, чем тело запроса на самом деле (фактически тела вообще не было)
  2. нижний сервис в сокете tomcat ожидал данные тела такого размера (http на TCP, который обеспечивает доставку с помощью инкапсуляции и ...)
  3. по истечении 60 секунд tomcat генерирует исключение тайм-аута: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception java.net.SocketTimeoutException: null
  4. клиент получает ответ с кодом состояния 500 из-за исключения тайм-аута.
  5. клиент закрывает соединение (потому что получает ответ).
  6. Кот бросает, java.net.SocketException: Broken pipeпотому что клиент закрыл его.

Иногда tomcat не генерирует исключение нарушенного пункта, потому что исключение тайм-аута закрывает соединение, поэтому такая разница меня смущает.


В Content-Typeзаголовке не указан номер, поэтому он не может быть «больше», чем все остальное. Исключения тайм-аута не закрывают сокет. Ответ не имеет никакого смысла.
Маркиз Лорн

@ EJP может быть, это была длина контента, не помню. Я думаю, что тайм-аут в Tomcat заставляет его возвращать 500, в то время как клиент получает этот ответ и закрывает соединение, что в итоге приводит к тому, что tomcat не получает достаточно байтов, как он ожидает.
Тийна

Если Tomcat отправляет 500, он уже получил все, что планирует. Все еще не имеет смысла.
маркиз Лорн

@ user207421 не совсем. Tomcat отправляет 500, потому что он не получает все, что он ожидает, но истекло время ожидания.
Тийна

-1

JavaDoc:

Максимальная длина очереди для индикаций входящего соединения (запроса на соединение) установлена ​​равной 50. Если индикация соединения приходит, когда очередь заполнена, соединение отклоняется.

Вы должны увеличить параметр "backlog" вашего ServerSocket, например

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

1
Увеличение отставания не решит ошибку «сломанной трубы».
маркиз Лорн

1
но это помогло мне
TheSecond

@EJP Я тестировал сервер в Linux, и он работал, но в Mac OS - нет. И увеличение размера отставания помогло мне. Я не знал, что максимальный размер равен 50.
Второе

1
Вы проверили что-то еще. Журнал ожидания не имеет ничего общего с этим исключением. Это помогает отказы соединения или тайм-ауты на клиенте. Исключения не «закрыты сокетом», которые являются локальной ошибкой.
маркиз Лорн

Для пулов сокетов (таких как HTTP-сервер, такой как Tomcat) существуют параметры конфигурации для числа ожидающих «принимает», сервер будет «стоять в очереди» перед отправкой обслуживающих потоков, и отдельный параметр для числа потоков в пуле. Однако все это не связано с тем, что после того, как Сервер "accept ()" установил соединение, выходной поток внезапно (невольно) "закрылся".
Даррелл Тиг

-1

Причина в том, что удаленный узел закрывает свой сокет (например, сбой), поэтому, если вы попытаетесь записать данные для него, например, вы получите это. чтобы решить эту проблему, защитите операции чтения / записи в сокет между (попытайтесь перехватить), затем перенесите случай потери соединения в перехват (обновить соединение, остановить программу и т. д.):

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

3
Это будет войти в проблемы, но это не будет решать их. Чтобы решить их, вы должны (а) устранить любые возможные ошибки протокола приложения и (б) закрыть мертвые соединения. Не только журнальные исключения, большинство из которых смертельно.
маркиз Лорн

@EJP: конечно, когда вы ловите ошибку, вы должны очистить свой сокет (контекст) в операторе catch. Я не могу догадаться, что разработчик собирается делать. он сам должен сделать чистую процедуру
elhadi dp ıpɐɥ ן ǝ

Разработчик должен исправить проблему протокола приложения, которая вызывает ошибку, и есть. Ничего, я ваш ответ или код, который помогает ему. Сделай так. «Вставить операции чтения / записи между» не исправляет это. Ваш ответ неверный.
Маркиз Лорн

@ user207421, разработчик спрашивает, как починить сломанную трубу. я указал ему, чтобы защитить в первую очередь свою программу с помощью (попробуйте ловить). это позволит избежать сбоя программы. тогда я вставляю комментарий, чтобы указать ему, чтобы сделать чистую обработку. Как правило, в случае ошибок в сломанной трубе существует много альтернатив, я не могу угадать цель программы, остановить программу, возобновить соединение, данные повреждены или нет .... и т. Д. это зависит от программиста, чтобы решить, какой вариант сделать? что не так с моим ответом?
elhadi dp ıpɐɥ ן ǝ
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.