วิธีการแก้ไข java.net.SocketException: Broken pipe?


150

ฉันใช้ apache คอมมอนส์ลูกค้า http เพื่อเรียก url โดยใช้วิธีการโพสต์เพื่อโพสต์พารามิเตอร์และมันก็โยนข้อผิดพลาดด้านล่างไม่ค่อย

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 ฝั่งเซิร์ฟเวอร์กำลังรับข้อยกเว้น Broken Pipe เพียงหมายความว่าเบราว์เซอร์ไคลเอ็นต์ออก / ไปยังหน้าอื่น / หมดเวลา / ย้อนกลับไปในประวัติศาสตร์ / อะไรก็ตาม แค่ลืมมัน
มาร์ควิสแห่ง Lorne

3
เราได้เห็นข้อยกเว้นนี้เมื่อยกเลิกคำขอ ajax ในเบราว์เซอร์ (โดยใช้XMLHttpRequest.abort())
ตรวจสอบ

2
สาเหตุหนึ่งที่เป็นไปได้คือพร็อกซีหรือเซิร์ฟเวอร์ Http ปิดการเชื่อมต่อเร็วเกินไป ตัวอย่างเช่นเซิร์ฟเวอร์ Apache Http ระหว่างเซิร์ฟเวอร์ J2EE ของคุณและแอปพลิเคชันไคลเอนต์โดยมีการหมดเวลาสั้น ๆ อย่างน้อยนั่นคือสิ่งที่เกิดขึ้นในสภาพแวดล้อมการผลิตของเรา ลูกค้าร้องเรียนเกี่ยวกับหน้าเว็บที่ไม่ได้รับการโหลดอย่างเต็มที่เราเห็นข้อผิดพลาดนี้ในบันทึก JBoss หลังจากการทดสอบบางอย่างเราสังเกตว่าปัญหาคือ Http Server ที่ตั้งค่าไว้ไม่ดี
Ricardo Vila

32

ในกรณีของเราเราพบสิ่งนี้ในขณะที่ทำการทดสอบโหลดบนเซิร์ฟเวอร์แอปของเรา ปัญหาปรากฎว่าเราต้องเพิ่มหน่วยความจำเพิ่มเติมใน JVM ของเราเพราะมันใกล้จะหมด วิธีนี้แก้ไขปัญหาได้

ลองเพิ่มหน่วยความจำที่มีให้กับ JVM และหรือตรวจสอบการใช้งานหน่วยความจำเมื่อคุณได้รับข้อผิดพลาดเหล่านั้น


4
ฉันได้รับปัญหาเดียวกันระหว่างการทดสอบโหลด ฉันเดาว่าข้อมูลต้องใช้เวลานานในการสร้างและเครื่องมือทดสอบโหลดไม่รอข้อมูลนานพอจากนั้นจะปิดการเชื่อมต่อ ในกรณีของคุณการเพิ่มหน่วยความจำอาจลดระยะเวลาในการสร้างข้อมูลเพื่อให้การทดสอบโหลดมีข้อมูลทั้งหมดโดยไม่ จำกัด ระยะเวลา ทางเลือกในการเพิ่มหน่วยความจำจะเป็นการเพิ่มการหมดเวลาของเครื่องมือทดสอบโหลด
Julien Kronegg

2
เห็นได้ชัดว่าหน่วยความจำเหลือน้อยทำให้แอปพลิเคชันปิดซ็อกเก็ตการรับหรือออกไปพร้อมกันโดยมีผลเหมือนกัน หน่วยความจำต่ำด้วยตัวเองไม่ได้ทำให้ท่อแตก
มาร์ควิสแห่ง Lorne

1
นี่ดูเหมือนจะเป็นปัญหาระหว่างการใช้งานหนักของเราเช่นกัน
Ruben de Vries

7

SocketException: ท่อที่ใช้งานไม่ได้เกิดจาก 'ปลายอีกด้าน' (ไคลเอนต์หรือเซิร์ฟเวอร์) ปิดการเชื่อมต่อในขณะที่รหัสของคุณกำลังอ่านหรือเขียนไปยังการเชื่อมต่อ

นี่เป็นข้อยกเว้นที่พบบ่อยมากในแอปพลิเคชันไคลเอนต์ / เซิร์ฟเวอร์ที่รับปริมาณการใช้งานจากไคลเอนต์หรือเซิร์ฟเวอร์นอกการควบคุมแอปพลิเคชัน ตัวอย่างเช่นลูกค้าคือเบราว์เซอร์ หากเบราว์เซอร์ทำการโทร Ajax และ / หรือผู้ใช้เพียงแค่ปิดหน้าเว็บหรือเบราว์เซอร์ก็จะสามารถฆ่าการสื่อสารทั้งหมดได้โดยไม่คาดคิด โดยพื้นฐานแล้วคุณจะเห็นข้อผิดพลาดนี้ได้ตลอดเวลาที่ปลายอีกด้านหนึ่งยกเลิกแอปพลิเคชันของพวกเขาและคุณไม่ได้คาดหวัง

หากคุณพบข้อยกเว้นนี้ในแอปพลิเคชันของคุณนั่นหมายความว่าคุณควรตรวจสอบรหัสของคุณเมื่อเกิดปัญหา IO (อินพุต / เอาท์พุต) แล้วปิดด้วย try / catch block เพื่อจับ IOException นี้ ถึงเวลาที่คุณจะต้องตัดสินใจว่าคุณต้องการจัดการกับสถานการณ์ที่ไม่ถูกต้องหรือไม่

ในกรณีของคุณสถานที่แรกสุดที่คุณยังคงมีการควบคุมคือการโทรHttpMethodDirector.executeWithRetry- ดังนั้นให้แน่ใจว่าการโทรนั้นถูกล้อมด้วย try / catch block และจัดการกับวิธีที่คุณเห็นว่าเหมาะสม

ฉันขอแนะนำอย่างยิ่งให้ป้องกันการบันทึกข้อผิดพลาดเฉพาะ 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

3
ฮ่า ๆ. คุณอาจเสียเวลาทั้งวันเพื่อแก้ไขคำแนะนำปลอมทั้งหมดใน SO
Dave

2
@Dave แน่นอน บางครั้งฉันก็ทำ
มาร์ควิสแห่ง Lorne

1

ฉันต้องการปัญหาเดียวกันในขณะที่ฉันกำลังพัฒนาแอปพลิเคชัน Java อย่างง่ายที่ฟังบน TCP เฉพาะ โดยปกติผมไม่มีปัญหา 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();

จาก Javadoc ของServerSocket :

ความยาวคิวสูงสุดสำหรับการบ่งชี้การเชื่อมต่อขาเข้า (การร้องขอเพื่อเชื่อมต่อ) ถูกตั้งค่าเป็นพารามิเตอร์ backlog หากการบ่งชี้การเชื่อมต่อมาถึงเมื่อคิวเต็มการเชื่อมต่อจะถูกปฏิเสธ


0

ปัญหาอาจเป็นได้ว่าไฟล์ที่ใช้งานของคุณไม่ได้รับการอัพเดตด้วยวิธีการ RMI ที่ถูกต้อง ตรวจสอบดูว่าอินเทอร์เฟซ RMI ของคุณมีพารามิเตอร์ที่อัปเดตหรือโครงสร้างข้อมูลที่ปรับปรุงซึ่งไคลเอ็นต์ของคุณไม่มี หรือว่าไคลเอนต์ RMI ของคุณไม่มีพารามิเตอร์ที่แตกต่างจากรุ่นเซิร์ฟเวอร์ของคุณ

นี่เป็นเพียงการเดาที่มีการศึกษา หลังจากการปรับใช้ไฟล์คลาสของแอปพลิเคชันเซิร์ฟเวอร์ของฉันอีกครั้งและการทดสอบอีกครั้งปัญหาของ "Broken pipe" หายไป


วิธีการ RMI อะไร ? RMI ไม่ได้กล่าวถึงในคำถาม
มาร์ควิสแห่ง Lorne

0

คำตอบข้างต้นแสดงให้เห็นถึงเหตุผลนี้java.net.SocketException: Broken pipe: อีกปลายปิดการเชื่อมต่อ ฉันต้องการแบ่งปันประสบการณ์สิ่งที่เกิดขึ้นเมื่อฉันพบมัน:

  1. ในคำขอของลูกค้าContent-Typeส่วนหัวจะถูกตั้งค่าให้มีขนาดใหญ่กว่าคำขอจริงโดยไม่ได้ตั้งใจคือ (อันที่จริงไม่มีเนื้อหาใด ๆ เลย)
  2. บริการด้านล่างในซ็อกเก็ต Tomcat กำลังรอข้อมูลขนาดตัวนั้น (http อยู่บน TCP ซึ่งทำให้มั่นใจได้ว่าการจัดส่งโดย encapsulating และ ... )
  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เพราะลูกค้าปิดมัน

บางครั้งแมวตัวผู้ไม่ได้โยนข้อยกเว้นจุดเล็ก ๆ ที่แตกหักเพราะข้อยกเว้นการหมดเวลาปิดการเชื่อมต่อทำไมความแตกต่างดังกล่าวทำให้ฉันสับสนเช่นกัน


Content-Typeส่วนหัวไม่ได้ระบุจำนวนจึงไม่สามารถเป็น 'ที่มีขนาดใหญ่กว่าอะไร ข้อยกเว้นการหมดเวลาอย่าปิดซ็อกเก็ต คำตอบไม่สมเหตุสมผลเลย
มาร์ควิสแห่ง Lorne

@EJP อาจเป็นความยาวของเนื้อหาจำไม่ได้ ฉันคิดว่าหมดเวลาใน Tomcat ทำให้มันกลับมา 500 ในขณะที่ลูกค้าได้รับการตอบสนองนี้และปิดการเชื่อมต่อซึ่งในที่สุดทำให้ Tomcat ไม่ได้รับไบต์เพียงพอตามที่คาดไว้
Tiina

ถ้า Tomcat ส่ง 500 มันได้รับทุกอย่างตามที่วางแผนไว้ ยังไม่สมเหตุสมผล
มาร์ควิสแห่ง Lorne

@ user207421 ไม่จริง Tomcat ส่ง 500 เนื่องจากไม่ได้รับทั้งหมดตามที่คาดหวัง แต่หมดเวลา
Tiina

-1

JavaDoc:

ความยาวคิวสูงสุดสำหรับการบ่งชี้การเชื่อมต่อขาเข้า (การร้องขอเพื่อเชื่อมต่อ) ถูกตั้งค่าเป็น 50 หากการบ่งชี้การเชื่อมต่อมาถึงเมื่อคิวเต็มการเชื่อมต่อจะถูกปฏิเสธ

คุณควรเพิ่มพารามิเตอร์ "backlog" ของ ServerSocket ของคุณ

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

1
การเพิ่มงานในมือจะไม่ช่วยแก้ปัญหาข้อผิดพลาด
มาร์ควิสแห่ง Lorne

1
แต่มันช่วยฉันได้
TheSecond

@EJP ฉันทดสอบเซิร์ฟเวอร์บน Linux และใช้งานได้ แต่บน Mac OS - ไม่ใช่ และการเพิ่มขนาดของงานในมือช่วยให้ฉัน ฉันไม่ทราบว่าขนาดสูงสุดคือ 50
TheSecond

1
คุณทดสอบอย่างอื่น ฟังสิ่งที่ค้างไม่มีอะไรเกี่ยวข้องกับข้อยกเว้นนี้ ช่วยในการปฏิเสธการเชื่อมต่อหรือหมดเวลาที่ลูกค้า ไม่ใช่ข้อยกเว้น 'ซ็อกเก็ตปิด' ซึ่งเป็นข้อผิดพลาดในเครื่อง
มาร์ควิสแห่ง Lorne

สำหรับซ็อกเก็ตพูล (เช่นเซิร์ฟเวอร์ HTTP เช่น Tomcat) มีการตั้งค่าการกำหนดค่าสำหรับจำนวนที่รอ "ยอมรับ" เซิร์ฟเวอร์จะ "คิว" ก่อนที่จะส่งเธรดการให้บริการและการตั้งค่าแยกต่างหากสำหรับจำนวนเธรดในพูล อย่างไรก็ตามไม่มีสิ่งใดที่เกี่ยวข้องกับปัญหาที่เกิดขึ้นหลังจากเซิร์ฟเวอร์ "accept ()" เมื่อทำการเชื่อมต่อ - กระแสเอาท์พุทจะเกิดขึ้นทันที (โดยไม่เจตนา) "ปิด"
Darrell Teague

-1

สาเหตุคือรีโมตเพียร์ปิดซ็อกเก็ต (ตัวอย่างเช่นขัดข้อง) ดังนั้นหากคุณพยายามเขียนข้อมูลกับเขาตัวอย่างเช่นคุณจะได้รับสิ่งนี้ เพื่อแก้ปัญหานั้นปกป้องการอ่าน / เขียนไปยังซ็อกเก็ตการดำเนินงานระหว่าง (ลองจับ) แล้วจัดการกรณีการเชื่อมต่อการสูญเสียลงในการจับ (ต่ออายุการเชื่อมต่อหยุดโปรแกรม ... ฯลฯ ):

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

3
นี้จะเข้าสู่ระบบปัญหาแต่มันจะไม่แก้ปัญหาให้พวกเขา ในการแก้ปัญหาคุณต้อง (a) แก้ไขข้อผิดพลาดใด ๆ ที่เป็นไปได้ของโปรโตคอลแอปพลิเคชันและ (b) ปิดการเชื่อมต่อที่ไม่ทำงาน ไม่ใช่แค่บันทึกข้อยกเว้นซึ่งส่วนใหญ่เป็นอันตรายถึงชีวิต
มาร์ควิสแห่ง Lorne

@EJP: แน่นอนเมื่อคุณพบข้อผิดพลาดคุณควรทำความสะอาดซ็อกเก็ต (บริบท) ของคุณลงในคำสั่ง catch ฉันไม่สามารถเดาได้ว่านักพัฒนาจะทำอะไร มันอยู่ที่ตัวเขาเองที่จะทำขั้นตอนการทำความสะอาด
elhadi DP ıpɐɥןǝ

มันขึ้นอยู่กับนักพัฒนาเพื่อแก้ไขปัญหาโปรโตคอลแอปพลิเคชันที่ทำให้เกิดข้อผิดพลาดและมี ฉันไม่มีคำตอบหรือรหัสของคุณที่ช่วยให้เขา ทำเช่นนั้น 'การแทรกอ่าน / เขียนระหว่าง' ไม่สามารถแก้ไขได้ คำตอบของคุณไม่ถูกต้อง
มาร์ควิสแห่ง Lorne

@ user207421, developper ถามวิธีการแก้ไขท่อแตก ฉันได้ระบุให้เขาเพื่อปกป้องโปรแกรมของเขาในตอนแรกโดยใช้ (ลองจับ) สิ่งนี้จะหลีกเลี่ยงโปรแกรมที่จะเกิดข้อผิดพลาด จากนั้นฉันใส่ความคิดเห็นเพื่อระบุให้เขาทำกระบวนการที่สะอาด โดยทั่วไปในข้อผิดพลาดไปป์ที่ขาดมีหลายทางเลือกฉันไม่สามารถเดาวัตถุประสงค์ของโปรแกรมหยุดโปรแกรมต่ออายุการเชื่อมต่อข้อมูลเสียหายหรือไม่ .... ฯลฯ ขึ้นอยู่กับโปรแกรมเมอร์ที่จะตัดสินใจว่าจะเลือกตัวเลือกใด? เกิดอะไรขึ้นกับคำตอบของฉัน
elhadi dp ıpɐɥןǝ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.