Як виправити java.net.SocketException: Зламана труба?


150

Я використовую клієнт apache commons 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 Server.
Рікардо Віла

32

У нашому випадку ми пережили це під час тестування завантаження на нашому сервері додатків. Проблема виявилася, що нам потрібно додати додаткову пам’ять до нашого JVM, оскільки він закінчувався. Це вирішило це питання.

Спробуйте збільшити об'єм пам'яті, доступної для JVM, або відслідковуйте використання пам'яті, коли ви отримуєте ці помилки.


4
Я отримав те саме питання під час тесту навантаження. Я здогадуюсь, що дані потребують багато часу для створення, і інструмент для перевірки навантаження не чекає даних досить довго, тоді він закриває з'єднання. У вашому випадку, додавання пам'яті, можливо, скоротило процес генерування даних, тому тест навантаження отримав усі дані без обмеження часу. Альтернативою для збільшення пам'яті було б збільшення тайм-ауту інструменту для перевірки навантаження.
Жульєн Кронегг

2
Очевидно, що низький обсяг пам’яті змусив програму закрити приймальний гніздо або взагалі вийти з таким же ефектом. Низька пам'ять сама по собі не викликає поломки труб.
Маркіз Лорн

1
це насправді є проблемою і під час нашої великої вантажопідйомності
Рубен де Вріс

7

SocketException: Зламана труба викликається тим, що "інший кінець" (Клієнт або сервер) закриває з'єднання, а ваш код читає або записує на з'єднання.

Це дуже поширений виняток у програмах клієнт / сервер, які отримують трафік від клієнтів або серверів поза контролем додатків. Наприклад, клієнт - це браузер. Якщо браузер здійснює дзвінок Ajax і / або користувач просто закриває сторінку або веб-переглядач, це може ефективно знищити все спілкування несподівано. По суті, ви побачите цю помилку кожного разу, коли інший кінець припинить їх застосування, і ви цього не очікували.

Якщо ви відчуваєте цей виняток у вашій програмі, це означає, що ви повинні перевірити свій код, де відбувається IO (Input / Output), і обгорнути його блоком спробу / catch, щоб отримати цю IOException. Саме тоді ви вирішуєте, як ви хочете впоратися з цією напівправдивою ситуацією.

У вашому випадку найранішим місцем, де ви все ще маєте контроль, є виклик HttpMethodDirector.executeWithRetry- Отже, переконайтеся, що виклик завершено блоком спробу / лову, і обробляйте його, як вважаєте за потрібне.

Я б настійно радив не записувати конкретні помилки SocketException-Broken Pipe за будь-яким іншим, ніж рівні налагодження / відстеження. Інше, це може бути використано як форма атаки DOS (Denial Of Service) шляхом заповнення журналів. Спробуйте загартувати та негативно перевірити свою заявку на цей загальний сценарій.


Це не викликано тим, що одноліткові закривають з'єднання під час читання з нього. Це спричиняє інший стан кінця потоку, який часто зовсім не є винятком.
Маркіз

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
Це насправді дуже дивно, тому що 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
@Dave Дійсно. Іноді я.
Маркіз Лорн

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 :

Максимальна довжина черги для індикацій вхідного з'єднання (запит на з'єднання) встановлюється параметром відсталого. Якщо індикатор підключення надходить, коли черга заповнена, у з’єднанні відмовляється.


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. tomcat кидає, java.net.SocketException: Broken pipeтому що клієнт його закрив.

Іноді tomcat не кидає виключений виправлений піп, оскільки виняток у тайм-ауті закриває з'єднання, чому така різниця бентежить і мене.


У Content-Typeзаголовку не вказано число, тому воно не може бути "більшим" ніж усе. Виключення з таймауту не закривають розетку. Відповідь не має жодного сенсу.
Маркіз Лорн

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

Якщо Tomcat надсилає 500, він уже отримав усе, що планує. Все ще немає сенсу.
Маркіз Лорн

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

-1

JavaDoc:

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

Наприклад, слід збільшити параметр "відставання" вашого ServerSocket

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

1
Збільшення відставання не вирішить помилку "зламаної труби".
Маркіз Лорн

1
але мені це допомогло
TheSecond

@EJP Я тестував сервер на Linux і він працював, але на Mac OS - ні. І збільшення розміру відставання допомогло мені. Я не знав, що максимальний розмір становить 50.
Друга

1
Ви випробували щось інше. Затримка прослуховування не має нічого спільного з цим винятком. Це допомагає відмовитися від з'єднання або таймауту у клієнта. Не є винятками з "socket closed", які є локальною помилкою.
Маркіз Лорн

Для пулів сокетів (таких як HTTP-сервер типу Tomcat) є налаштування конфігурації для кількості очікуючих "приймає", сервер буде "в черзі" перед відправленням потоків обслуговування та окремий параметр для кількості потоків у пулі. Однак нічого з цього не пов’язане з проблемою, що після того, як Сервер "прийме ()", коли з'єднання встановлено - вихідний потік раптово (мимоволі) "закриється".
Даррелл Тейг

-1

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

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

3
Це буде увійти в проблеми, але це не буде вирішувати їх. Для їх вирішення необхідно (a) усунути всі можливі помилки протоколу програми та (b) закрити мертві з'єднання. Не лише винятки з журналу, більшість з яких є смертельними.
Маркіз Лорн

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

Розробник повинен виправити проблему з протоколом програми, яка спричиняє помилку. Нічого, я вам відповідь чи код, що не допомагає йому. зроби так. "Вставити операції читання / запису між" не виправить це. Ваша відповідь неправильна.
Маркіз Лорн

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