Jak naprawić java.net.SocketException: Broken pipe?


150

Używam klienta http apache commons do wywołania adresu URL za pomocą metody post, aby opublikować parametry i rzadko rzuca poniższy błąd.

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)

Czy ktoś może zasugerować, co powoduje ten wyjątek i jak go debugować?


Odpowiedzi:


81

Jest to spowodowane:

  • najczęściej pisząc do połączenia, gdy drugi koniec już je zamknął;
  • rzadziej peer zamykający połączenie bez odczytu wszystkich danych, które już czekają na jego koniec.

W obu przypadkach masz źle zdefiniowany lub zaimplementowany protokół aplikacji.

Jest trzeci powód, którego tutaj nie będę dokumentował, ale który polega na tym, że peer podejmuje celowe działanie, aby zresetować, a nie prawidłowo zamknąć połączenie.


4
Czy możesz mi pomóc zidentyfikować i poprawić to? Zasadniczo, jak potwierdzić przyczynę i naprawić?

4
I zostały potwierdzone przyczyny. Piszesz, podczas gdy drugi koniec już zamknął połączenie. Rozwiązaniem jest nie robić tego. Ponieważ drugi koniec go nie czyta, i tak nie ma sensu. Jak powiedziałem również, jeśli tak się dzieje, coś jest nie tak ze specyfikacją lub implementacją protokołu aplikacji, najprawdopodobniej nawet takiego nie masz.
Markiz Lorne

26
Jeśli aplikacja HTTP po stronie serwera otrzymuje wyjątki zepsutego potoku, oznacza to po prostu, że przeglądarka klienta opuściła / przeszła na inną stronę / przekroczyła limit czasu / cofnęła się do historii / cokolwiek. Po prostu zapomnij o tym.
Markiz Lorne

3
Widzieliśmy ten wyjątek podczas przerywania żądania AJAX w przeglądarce (przy użyciu XMLHttpRequest.abort()).
obecker

2
Jedną z możliwych przyczyn może być zbyt wczesne zamknięcie połączenia przez serwer proxy lub serwer HTTP. Na przykład serwer Apache Http między serwerem J2EE a klientami aplikacji, z krótkim, zdefiniowanym limitem czasu. Tak przynajmniej stało się w naszym środowisku produkcyjnym. Klienci skarżą się, że strony nie są w pełni ładowane i widzieliśmy ten błąd w dzienniku JBoss. Po pewnym teście zauważyliśmy, że problemem był źle skonfigurowany serwer HTTP.
Ricardo Vila,

32

W naszym przypadku doświadczyliśmy tego podczas wykonywania testu obciążenia na naszym serwerze aplikacji. Okazało się, że musimy dodać dodatkową pamięć do naszej maszyny JVM, ponieważ kończyła się. To rozwiązało problem.

Spróbuj zwiększyć ilość pamięci dostępnej dla maszyny JVM i / lub monitoruj użycie pamięci w przypadku wystąpienia tych błędów.


4
Ten sam problem wystąpił podczas testu obciążenia. Wydaje mi się, że wygenerowanie danych zajmuje dużo czasu, a narzędzie do testowania obciążenia nie czeka na dane wystarczająco długo, a następnie zamyka połączenie. W twoim przypadku dodanie pamięci mogło skrócić czas trwania procesu generowania danych, więc test obciążenia uzyskał wszystkie dane bez limitu czasu. Alternatywą dla zwiększenia pamięci byłoby zwiększenie limitu czasu narzędzia do testów obciążenia.
Julien Kronegg

2
Najwyraźniej mała ilość pamięci spowodowała, że ​​aplikacja zamknęła gniazdo odbierające lub całkowicie zakończyła pracę, z tym samym skutkiem. Niska pamięć sama w sobie nie powoduje pękania rur.
Markiz Lorne

1
wydaje się, że jest to problem również podczas naszej aplikacji o dużym obciążeniu
Ruben de Vries

7

SocketException: Przerwany potok jest spowodowany przez „drugi koniec” (klient lub serwer) zamykający połączenie, podczas gdy kod odczytuje z połączenia lub zapisuje do niego.

Jest to bardzo częsty wyjątek w aplikacjach typu klient / serwer, które odbierają ruch od klientów lub serwerów poza kontrolą aplikacji. Na przykład klient jest przeglądarką. Jeśli przeglądarka wykonuje wywołanie Ajax i / lub użytkownik po prostu zamyka stronę lub przeglądarkę, może to skutecznie nieoczekiwanie przerwać całą komunikację. Zasadniczo zobaczysz ten błąd za każdym razem, gdy drugi koniec zakończy swoją aplikację, a ty tego nie przewidziałeś.

Jeśli wystąpi ten wyjątek w aplikacji, oznacza to, że należy sprawdzić kod, w którym występuje IO (wejście / wyjście) i zawinąć go blokiem try / catch, aby przechwycić ten wyjątek IOException. To do Ciebie należy decyzja, jak postąpić w tej pół-prawidłowej sytuacji.

W twoim przypadku najwcześniejszym miejscem, w którym nadal masz kontrolę, jest wywołanie HttpMethodDirector.executeWithRetry- więc upewnij się, że połączenie jest opakowane blokiem try / catch i postępuj z nim tak, jak uważasz za stosowne.

Zdecydowanie odradzam rejestrowanie błędów specyficznych dla SocketException-Broken Pipe na jakichkolwiek innych poziomach niż debug / trace. W przeciwnym razie można to wykorzystać jako formę ataku DOS (Denial Of Service) poprzez wypełnienie dzienników. Spróbuj wzmocnić i przetestować aplikację pod kątem tego typowego scenariusza.


Nie jest to spowodowane tym, że peer zamyka połączenie podczas czytania z niego. To powoduje inny, koniec strumienia, stan, który często nie jest wyjątkiem.
Markiz Lorne

5

Wszystkie otwarte strumienie i połączenia muszą być prawidłowo zamknięte, więc przy następnej próbie użycia obiektu urlConnection nie generuje on błędu. Na przykład następująca zmiana kodu naprawiła błąd.

Przed:

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

Po:

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
W rzeczywistości jest to bardzo dziwne, ponieważ a BufferedOutputStreamzawsze wywołuje close()swój podstawowy strumień wyjściowy (a więc strumień wyjściowy urlConnection). Zobacz docs.oracle.com/javase/6/docs/api/java/io/… i docs.oracle.com/javase/6/docs/api/java/io/... Więc kiedy dzwonisz os.close(), powinno być już zamknięte . Nie?
obecker

4
„os.close ()” nie jest „koniecznością”. Tak samo nie jest „out.close ()”. „bw.close ()” jest wystarczające. @obedker ma rację. Sama ta zmiana nie mogła rozwiązać problemu.
Markiz Lorne

1

Zaimplementowałem funkcję pobierania danych przez serwer FTP i znalazłem tam ten sam wyjątek podczas wznawiania tego pobierania. Aby rozwiązać ten wyjątek, zawsze będziesz musiał rozłączyć się z poprzednią sesją i utworzyć nową instancję klienta oraz nowe połączenie z serwerem. To samo podejście może być pomocne również w przypadku HTTPClient.


Aby rozwiązać ten błąd, musisz unikać używania zamkniętych gniazd.
Markiz Lorne

3
LOL. Możesz zmarnować cały dzień poprawiając wszystkie fałszywe porady dotyczące SO.
Dave,

2
@Dave Rzeczywiście. Czasami robię.
Markiz Lorne

1

Miałem ten sam problem, kiedy tworzyłem prostą aplikację Java, która nasłuchuje na określonym TCP. Zwykle nie miałem problemu, ale po uruchomieniu testu warunków skrajnych zauważyłem, że niektóre połączenie zrywało się z błędem socket write exception.

Po dochodzeniu znalazłem rozwiązanie, które rozwiązuje mój problem. Wiem, że to pytanie jest dość stare, ale wolę podzielić się moim rozwiązaniem, ktoś może uznać je za przydatne.

Problem dotyczył tworzenia ServerSocket. Czytałem z Javadoc, że jest domyślny limit 50 oczekujących gniazd. Jeśli spróbujesz otworzyć inne połączenie, zostaną one odrzucone. Rozwiązanie polega po prostu na zmianie tej domyślnej konfiguracji po stronie serwera. W poniższym przypadku tworzę serwer Socket, który nasłuchuje na porcie TCP 10_000i akceptuje maksymalną 200liczbę oczekujących gniazd.

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

Z Javadoc ServerSocket :

Maksymalna długość kolejki dla wskazań połączeń przychodzących (żądanie połączenia) jest ustawiona w parametrze backlog. Jeśli wskazanie połączenia pojawi się, gdy kolejka jest pełna, połączenie jest odrzucane.


0

Problem może polegać na tym, że wdrożone pliki nie są aktualizowane przy użyciu poprawnych metod RMI. Sprawdź, czy interfejs RMI ma zaktualizowane parametry lub zaktualizowane struktury danych, których nie ma Twój klient. Lub że twój klient RMI nie ma parametrów różniących się od tych, które ma twoja wersja serwera.

To tylko zgadywanie. Po ponownym wdrożeniu plików klas mojej aplikacji serwerowej i ponownym przetestowaniu problem „zepsutej rury” zniknął.


Jakie metody RMI? RMI nie jest wymienione w pytaniu.
Markiz Lorne,

0

Powyższe odpowiedzi ilustrują przyczynę tego java.net.SocketException: Broken pipe: drugi koniec zamknął połączenie. Chciałbym podzielić się doświadczeniem, co się stało, gdy go spotkałem:

  1. w żądaniu klienta Content-Typenagłówek jest omyłkowo ustawiony jako większy niż w rzeczywistości jest treść żądania (w rzeczywistości nie było jej wcale)
  2. dolna usługa w gnieździe tomcat czekała na dane body o takim rozmiarze (http jest na TCP, co zapewnia dostarczanie przez hermetyzację i ...)
  3. po upływie 60 sekund tomcat wyrzuca wyjątek limitu czasu: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception java.net.SocketTimeoutException: null
  4. klient otrzymuje odpowiedź o kodzie stanu 500 z powodu przekroczenia limitu czasu.
  5. klient zamyka połączenie (ponieważ otrzymuje odpowiedź).
  6. Tomcat wyrzuca, java.net.SocketException: Broken pipeponieważ klient go zamknął.

Czasami tomcat nie zgłasza zepsutego wyjątku pip, ponieważ wyjątek timeout zamyka połączenie, dlaczego taka różnica też mnie dezorientuje.


Content-TypeNagłówka nie podaje numeru, więc to nie może być „większy” niż cokolwiek innego. Wyjątki przekroczenia limitu czasu nie zamykają gniazda. Odpowiedź nie ma żadnego sensu.
Markiz Lorne,

@EJP może to była długość treści, nie pamiętam. Myślę, że przekroczenie limitu czasu w Tomcat sprawia, że ​​zwraca 500, podczas gdy klient otrzymuje tę odpowiedź i zamyka połączenie, co ostatecznie powoduje, że tomcat nie otrzymuje wystarczającej liczby bajtów, jak się spodziewa.
Tiina,

Jeśli Tomcat wyśle ​​500, otrzyma już wszystko, co planuje. Nadal nie ma sensu.
Markiz Lorne

@ user207421 nie bardzo. Tomcat wysyła 500, ponieważ nie otrzymał wszystkiego, czego oczekuje, ale przekroczył limit czasu.
Tiina

-1

JavaDoc:

Maksymalna długość kolejki dla wskazań połączenia przychodzącego (żądanie połączenia) jest ustawiona na 50. Jeśli wskazanie połączenia pojawia się, gdy kolejka jest pełna, połączenie jest odrzucane.

Na przykład należy zwiększyć parametr „backlog” swojego serwera ServerSocket

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

1
Zwiększenie zaległości nie rozwiąże błędu „pękniętej rury”.
Markiz Lorne,

1
ale pomogło mi
TheSecond

@EJP Przetestowałem serwer na Linuksie i zadziałał, ale na Mac OS - nie. Pomogło mi zwiększenie ilości zaległości. Nie wiedziałem, że maksymalny rozmiar to 50.
Drugi

1
Przetestowałeś coś innego. Lista zaległości odsłuchu nie ma nic wspólnego z tym wyjątkiem. Pomaga w odmowie połączenia lub przekroczeniu limitu czasu u klienta. Nie wyjątki „gniazdo zamknięte”, które są błędem lokalnym.
Markiz Lorne

W przypadku pul gniazd (takich jak serwer HTTP, taki jak Tomcat) istnieją ustawienia konfiguracyjne dotyczące liczby oczekujących „akceptacji”, które serwer będzie „ustawiać w kolejce” przed wysłaniem wątków obsługujących, oraz osobne ustawienie liczby wątków w puli. Jednak nic z tego nie jest związane z problemem, że po „accept ()” serwera podczas nawiązywania połączenia - strumień wyjściowy zostaje nagle (nieświadomie) „zamknięty”.
Darrell Teague

-1

Przyczyną jest to, że zdalny peer zamyka swoje gniazdo (na przykład awaria), więc jeśli spróbujesz na przykład zapisać do niego dane, otrzymasz to. aby rozwiązać ten problem, zabezpiecz operacje odczytu / zapisu do gniazda między (try catch), a następnie zarządzaj przypadkiem utraty połączenia w catch (odnów połączenie, zatrzymaj program itp.):

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

3
Będzie to zalogować się problemy, ale to nie rozwiązuje je. Aby je rozwiązać , musisz (a) rozwiązać wszelkie możliwe błędy protokołu aplikacji i (b) zamknąć martwe połączenia. Nie tylko rejestrowanie wyjątków, z których większość jest śmiertelna.
Markiz Lorne,

@EJP: oczywiście, gdy złapiesz błąd, powinieneś wyczyścić gniazdo (kontekst) w instrukcji catch. Nie mogę zgadnąć, co zrobi programista. sam wykonuje procedurę czyszczenia
elhadi dp ıpɐɥ ן ǝ

Do programisty należy naprawienie problemu z protokołem aplikacji, który powoduje błąd, i tak jest. Nic, co jest Twoją odpowiedzią lub kodem, nie pomoże mu. Zrób tak. „Wstaw operacje odczytu / zapisu między” nie rozwiązuje tego problemu. Twoja odpowiedź jest nieprawidłowa.
Markiz Lorne

@ user207421, programista pyta, jak naprawić uszkodzoną rurę. Wskazałem mu, aby najpierw zabezpieczył swój program za pomocą (spróbuj złapać). pozwoli to uniknąć awarii programu. następnie wstawiam komentarz, aby wskazać mu, że ma wykonać czyste przetwarzanie. Generalnie w błędach zepsutego przewodu jest wiele alternatyw, nie mogę odgadnąć celu programu, zatrzymać program, odnowić połączenie, dane są uszkodzone lub nie ... itd. to do programisty należy decyzja, którą opcję zrobić? co było nie tak z moją odpowiedzią?
elhadi dp ıpɐɥ ן ǝ
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.