java.net.SocketException 수정하는 방법 : 깨진 파이프?


150

매개 변수를 게시하기 위해 post 메소드를 사용하여 URL을 호출하기 위해 Apache Commons http 클라이언트를 사용하고 있으며 아래 오류가 거의 발생하지 않습니다.

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)

누군가이 예외의 원인과 디버깅 방법을 제안 할 수 있습니까?


1
이 시도하는 것이 가장 좋습니다 stackoverflow.com/questions/10142409/...을
Vimalkumar Natarajan를

답변:


81

이 원인은 다음과 같습니다.

  • 가장 일반적으로, 다른 쪽 끝이 이미 닫 혔을 때 연결에 쓰는 것;
  • 덜 일반적으로, 피어는 끝에서 이미 보류중인 모든 데이터를 읽지 않고 연결을 닫습니다.

따라서 두 경우 모두 응용 프로그램 프로토콜이 잘못 정의되었거나 구현되었습니다.

여기에 문서화하지는 않지만 연결을 올바르게 닫지 않고 재설정하기 위해 의도적으로 조치를 취하는 피어와 관련된 세 번째 이유가 있습니다.


4
식별하고 수정하도록 도와 주실 수 있습니까? 기본적으로 이유를 확인하고 수정하는 방법은 무엇입니까?

4
나는 이유를 확인했다. 다른 쪽 끝이 이미 연결을 닫고있는 동안 쓰고 있습니다. 해결은 그렇게하지 않습니다. 다른 쪽 끝은 읽지 않으므로 어쨌든 요점은 없습니다. 내가 말했듯이, 이것이 일어나면 응용 프로그램 프로토콜 사양이나 구현에 문제가있을 수 있습니다. 아마도 하나도 없습니다.
Lorne의 후작

26
서버 측 HTTP 응용 프로그램에서 파이프 중단 예외가 발생하면 클라이언트 브라우저가 종료되었거나 다른 페이지로 이동하거나 시간 초과 / 기록에서 사라졌습니다. 그냥 잊어 버려.
Lorne의 후작

3
브라우저에서 ajax 요청을 중단 할 때 (을 사용하여 XMLHttpRequest.abort()) 이 예외가 발생했습니다 .
obecker

2
가능한 원인 중 하나는 프록시 또는 Http 서버가 연결을 너무 일찍 닫는 것입니다. 예를 들어 J2EE 서버와 애플리케이션 클라이언트 사이에 Apache Http 서버가 정의되어 있으며 시간이 짧습니다. 적어도 그것은 프로덕션 환경에서 일어난 일이었습니다. 페이지가 완전히로드되지 않은 것에 대한 고객의 불만이 JBoss 로그에서이 오류를 확인했습니다. 테스트를 마친 후에는 문제가 제대로 구성된 Http Server 인 것으로 나타났습니다.
Ricardo Vila

32

우리의 경우 앱 서버에서로드 테스트를 수행하는 동안이 문제가 발생했습니다. 이 문제는 JVM에 메모리가 부족하여 추가 메모리를 추가해야한다는 것이 밝혀졌습니다. 문제가 해결되었습니다.

JVM에 사용 가능한 메모리를 늘리거나 해당 오류가 발생하면 메모리 사용량을 모니터하십시오.


4
로드 테스트 중에 같은 문제가 발생했습니다. 데이터를 생성하는 데 많은 시간이 걸리고로드 테스트 도구가 데이터를 충분히 오래 기다리지 않으면 연결이 닫힙니다. 귀하의 경우, 메모리를 추가하면 데이터 생성 프로세스 기간이 단축되어로드 테스트가 시간 초과 제한없이 모든 데이터를 얻었습니다. 메모리를 늘리는 대안은로드 테스트 툴 타임 아웃을 늘리는 것입니다.
Julien Kronegg

2
분명히 메모리 부족으로 인해 응용 프로그램이 수신 소켓을 닫거나 실제로 같은 결과를 얻었습니다. 메모리 부족해도 파이프가 파손되지 않습니다.
Lorne의 후작

1
이것은 실제로 우리의 고하 중 응용 프로그램에서도 문제가되는 것으로 보입니다
Ruben de Vries

7

SocketException : 끊어진 파이프는 코드에서 연결을 읽거나 쓰는 동안 '다른 쪽'(클라이언트 또는 서버)이 연결을 닫아서 발생합니다.

응용 프로그램 제어 외부의 클라이언트 또는 서버에서 트래픽을 수신하는 클라이언트 / 서버 응용 프로그램에서는 매우 일반적인 예외입니다. 예를 들어 클라이언트는 브라우저입니다. 브라우저가 Ajax 호출을하거나 사용자가 단순히 페이지 나 브라우저를 닫으면 모든 통신이 예기치 않게 중단 될 수 있습니다. 기본적으로, 다른 쪽 끝이 응용 프로그램을 종료 할 때마다이 오류가 표시되며 예상하지 않았습니다.

애플리케이션에서이 예외가 발생하면 IO (입력 / 출력)가 발생하는 코드를 확인하고 try / catch 블록으로 랩하여이 IOException을 포착해야 함을 의미합니다. 이 반 유효한 상황을 어떻게 처리할지 결정하는 것은 사용자의 책임입니다.

귀하의 경우, 귀하가 여전히 통제 할 수있는 가장 빠른 장소는 HttpMethodDirector.executeWithRetry 따라서 try / catch 블록으로 전화를 감싸고 귀하가 적합한 방식으로 처리하도록하십시오.

디버그 / 추적 레벨 이외의 다른 곳에서 SocketException-Broken Pipe 특정 오류를 기록하지 않도록 강력히 권장합니다. 그렇지 않으면 로그를 채워서 DOS (서비스 거부) 공격의 형태로 사용할 수 있습니다. 이 일반적인 시나리오에 대해 애플리케이션을 시험해보고 강화하십시오.


피어가 연결을 읽는 동안 연결을 닫아서 발생하는 것은 아닙니다. 이로 인해 다른 스트림 끝 조건이 발생하며 이는 종종 예외가 아닙니다.
Lorne의 후작

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가 정확합니다. 이 변경만으로는 문제를 해결할 수 없습니다.
Lorne의 후작

1

FTP 서버를 통해 데이터 다운로드 기능을 구현했으며 해당 다운로드를 다시 시작하는 동안 동일한 예외가 발견되었습니다. 이 예외를 해결하려면 항상 이전 세션에서 연결을 끊고 클라이언트의 새 인스턴스와 서버와의 새 연결을 작성해야합니다. 이와 동일한 접근 방식이 HTTPClient에도 도움이 될 수 있습니다.


이 오류를 해결하려면 닫힌 소켓을 사용하지 마십시오.
Lorne의 후작 19.38에

3
LOL. SO에 대한 모든 가짜 조언을 수정하여 하루 종일 낭비 할 수 있습니다.
Dave

2
@Dave Indeed. 때때로 나는한다.
Lorne의 후작

1

특정 TCP에서 수신 대기하는 간단한 Java 응용 프로그램을 개발하는 동안 동일한 문제가 발생했습니다. 일반적으로 문제가 없었지만 스트레스 테스트를 실행하면 일부 연결에서 오류가 발생했습니다.socket write exception .

조사 후 내 문제를 해결하는 솔루션을 찾았습니다. 나는이 질문이 꽤 오래되었다는 것을 알고 있지만 내 솔루션을 공유하는 것을 선호합니다. 누군가 유용 할 수 있습니다.

문제는 ServerSocket 작성에있었습니다. Javadoc에서 50 개의 보류중인 소켓의 기본 제한이 있다고 읽었습니다. 다른 연결을 열려고하면 연결이 거부됩니다. 솔루션은 단순히 서버 측에서이 기본 구성을 변경하는 것으로 구성됩니다. 다음 경우에는 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();

ServerSocket의 Javadoc에서 :

들어오는 연결 표시 (연결 요청)의 최대 큐 길이는 백 로그 매개 변수로 설정됩니다. 큐가 가득 찼을 때 연결 표시가 도착하면 연결이 거부됩니다.


0

배포 된 파일이 올바른 RMI 방법으로 업데이트되지 않았을 수 있습니다. RMI 인터페이스에 업데이트 된 매개 변수 또는 클라이언트에없는 데이터 구조가 업데이트되었는지 확인하십시오. 또는 RMI 클라이언트에 서버 버전과 다른 매개 변수가 없습니다.

이것은 단지 교육받은 추측 일뿐입니다. 서버 응용 프로그램의 클래스 파일을 다시 배포하고 다시 테스트 한 후 "깨진 파이프"문제가 사라졌습니다.


어떤 RMI 방법? RMI는 질문에 언급되지 않았습니다.
Lorne의 후작

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클라이언트가 닫았 기 때문에 바람둥이가 던집니다 .

때로는 시간 초과 예외가 연결을 닫기 때문에 바람둥이가 깨진 핍 예외를 throw하지 않습니다. 왜 그런 차이가 저를 혼란스럽게합니까?


Content-Type은 무엇보다 '더'가 될 수 있도록 헤더, 수를 지정하지 않습니다. 시간 종료 예외는 소켓을 닫지 않습니다. 대답은 전혀 이해가되지 않습니다.
Lorne의 후작

@ EJP는 아마도 콘텐츠 길이 일 것입니다. 기억하지 마십시오. Tomcat의 시간 초과로 인해 500이 반환되고 클라이언트 가이 응답을 받고 연결을 닫으면 Tomcat이 예상대로 충분한 바이트를받지 못하게됩니다.
Tiina

Tomcat이 500을 보내면 이미 계획 한 모든 것을 받았습니다. 여전히 이해가되지 않습니다.
Lorne의 후작

@ user207421 실제로는 아닙니다. Tomcat은 예상 한 모든 것을받지 못하고 시간 초과 되었기 때문에 500을 보냅니다.
Tiina

-1

JavaDoc :

들어오는 연결 표시 (연결 요청)의 최대 큐 길이는 50으로 설정됩니다. 큐가 가득 찼을 때 연결 표시가 도착하면 연결이 거부됩니다.

예를 들어 ServerSocket의 "백 로그"매개 변수를 늘려야합니다.

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

1
백 로그를 늘리면 '브로큰 파이프'오류가 해결되지 않습니다.
Lorne의 후작

1
그러나 그것은 나를 위해 도움이되었습니다
TheSecond

@ EJP Linux에서 서버를 테스트했지만 작동했지만 Mac OS에서는 그렇지 않습니다. 백 로그 크기를 늘리면 도움이되었습니다. 나는 최대 크기가 50이라는 것을 몰랐습니다.
TheSecond

1
다른 것을 테스트했습니다. 수신 백로 그는이 예외와 관련이 없습니다. 클라이언트에서 연결 거부 또는 시간 초과를 돕습니다. 로컬 소켓 오류 인 '소켓 닫힘'예외는 아닙니다.
Lorne의 후작

소켓 풀 (예 : Tomcat과 같은 HTTP 서버)의 경우 보류중인 "수락"수에 대한 구성 설정이 있으며 서비스 스레드를 전달하기 전에 서버가 "대기"하고 풀의 스레드 수에 대한 별도의 설정이 있습니다. 그러나,이 중 어느 것도 그 문제와 관련되지 않은 후에 접속이 확립 될 때 서버 "수용 ()"- 출력 스트림 (무의식적) "닫힌"갑자기.
Darrell Teague

-1

원인은 원격 피어가 소켓을 닫는 것입니다 (예 : 충돌). 예를 들어 데이터를 쓰려고하면이 오류가 발생합니다. 이를 해결하려면 (시도 캐치) 사이의 소켓에 대한 읽기 / 쓰기 보호를 시도한 다음 연결 끊기 사례를 캐치로 관리하십시오 (연결 갱신, 프로그램 중지 등).

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

3
이것은 것이다 로그인 문제, 하지만하지 않습니다 해결 을. 이를 해결 하려면 (a) 가능한 응용 프로그램 프로토콜 오류를 해결하고 (b) 연결이 끊어진 연결을 닫아야 합니다. 예외를 기록 할뿐만 아니라 대부분은 치명적입니다.
Lorne의 후작

@EJP : 물론 오류가 발생하면 catch 문에 소켓 (컨텍스트)을 정리해야합니다. 개발자가 무엇을할지 추측 할 수 없습니다. 깨끗한 절차를 수행하는 것은 그 자신의
책임입니다

오류를 발생시키는 응용 프로그램 프로토콜 문제를 해결하는 것은 개발자의 책임입니다. 그에게 도움이되는 답변이나 코드는 없습니다. 그렇게하세요. '사이에 읽기 / 쓰기 작업 삽입'으로 해결되지 않습니다. 대답이 틀립니다.
Lorne의 후작

@ user207421에서 개발자는 깨진 파이프를 수정하는 방법을 묻습니다. 나는 먼저 (캐치 시도)를 사용하여 그의 프로그램을 보호하라고 지시했다. 이것은 프로그램 충돌을 피할 것입니다. 그런 다음 깨끗한 처리를 수행하기 위해 주석을 삽입합니다. 일반적으로 깨진 파이프 오류에는 많은 대안이 있습니다. 프로그램의 목적을 추측하거나, 프로그램을 중지하고, 연결을 갱신하고, 데이터가 손상되었는지 여부 등을 확인할 수 없습니다. 수행 할 옵션을 결정하는 것은 프로그래머에게 달려 있습니까? 내 대답에 무엇이 잘못 되었습니까?
elhadi dp ıpɐɥ ן ǝ
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.