Làm cách nào để sửa java.net.SocketException: Hỏng đường ống?


150

Tôi đang sử dụng apache commons http client để gọi url bằng phương thức post để đăng các tham số và nó hiếm khi bị lỗi dưới đây.

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)

Ai đó có thể đề xuất những gì gây ra Ngoại lệ này và làm thế nào để gỡ lỗi nó?


Câu trả lời:


81

Điều này được gây ra bởi:

  • thông thường nhất, viết vào một kết nối khi đầu kia đã đóng nó;
  • ít thường xuyên hơn, đồng nghiệp đóng kết nối mà không đọc tất cả dữ liệu đang chờ xử lý ở cuối.

Vì vậy, trong cả hai trường hợp, bạn có một giao thức ứng dụng được xác định hoặc thực hiện kém.

Có một lý do thứ ba mà tôi sẽ không ghi lại ở đây nhưng liên quan đến hành động cố tình ngang hàng để thiết lập lại thay vì đóng kết nối đúng cách.


4
Bạn có thể vui lòng giúp tôi xác định và sửa chữa nó? Về cơ bản, làm thế nào để xác nhận lý do và khắc phục?

4
Tôi đã xác nhận lý do. Bạn đang viết trong khi đầu kia đã đóng kết nối. Việc khắc phục là không làm điều đó. Vì đầu bên kia không đọc nó, dù sao cũng không có điểm nào. Như tôi cũng đã nói, nếu điều này xảy ra thì có gì đó không đúng với đặc tả hoặc cách thực hiện giao thức ứng dụng của bạn, rất có thể là bạn thậm chí không có.
Hầu tước Lorne

26
Nếu ứng dụng HTTP phía máy chủ nhận được ngoại lệ Broken Tube, điều đó chỉ có nghĩa là trình duyệt máy khách đã thoát / chuyển sang trang khác / hết thời gian / quay lại trong lịch sử / bất cứ điều gì. Hãy quên nó đi.
Hầu tước Lorne

3
Chúng tôi đã thấy ngoại lệ này khi hủy yêu cầu ajax trong trình duyệt (sử dụng XMLHttpRequest.abort()).
phản đối

2
Một nguyên nhân có thể là proxy hoặc máy chủ http đóng kết nối quá sớm. Ví dụ: máy chủ Apache Apache giữa máy chủ J2EE của bạn và các máy khách ứng dụng, với thời gian chờ được xác định ngắn. Ít nhất đó là những gì đã xảy ra trên môi trường sản xuất của chúng tôi. Khách hàng phàn nàn về các trang không được tải đầy đủ, chúng tôi đã thấy lỗi này trên nhật ký JBoss. Sau một số thử nghiệm, chúng tôi nhận thấy rằng sự cố là Máy chủ http có cấu hình kém.
Ricardo Vila

32

Trong trường hợp của chúng tôi, chúng tôi đã trải nghiệm điều này trong khi thực hiện kiểm tra tải trên máy chủ ứng dụng của mình. Vấn đề hóa ra là chúng ta cần thêm bộ nhớ bổ sung vào JVM của mình vì nó đã hết. Điều này đã giải quyết vấn đề.

Hãy thử tăng bộ nhớ khả dụng cho JVM và hoặc theo dõi việc sử dụng bộ nhớ khi bạn gặp những lỗi đó.


4
Tôi đã gặp vấn đề tương tự trong một bài kiểm tra tải. Tôi đoán dữ liệu cần rất nhiều thời gian để tạo và công cụ kiểm tra tải không chờ dữ liệu đủ lâu sau đó sẽ đóng kết nối. Tôi là trường hợp của bạn, việc thêm bộ nhớ có thể đã giảm thời lượng quá trình tạo dữ liệu để kiểm tra tải có tất cả dữ liệu mà không giới hạn thời gian chờ. Một cách khác để tăng bộ nhớ sẽ là tăng thời gian chờ của công cụ kiểm tra tải.
Julien Kronegg

2
Rõ ràng bộ nhớ thấp khiến ứng dụng đóng ổ cắm nhận hoặc thực sự thoát hoàn toàn, với cùng một hiệu ứng. Bộ nhớ thấp tự nó không gây ra vỡ ống.
Hầu tước Lorne

1
đây thực sự có vẻ là một vấn đề trong ứng dụng tải nặng của chúng tôi
Ruben de Vries

7

SocketException: Đường ống bị hỏng, là do 'đầu kia' (Máy khách hoặc máy chủ) đóng kết nối trong khi mã của bạn đang đọc từ hoặc ghi vào kết nối.

Đây là một ngoại lệ rất phổ biến trong các ứng dụng khách / máy chủ nhận lưu lượng truy cập từ máy khách hoặc máy chủ bên ngoài kiểm soát ứng dụng. Ví dụ, khách hàng là một trình duyệt. Nếu trình duyệt thực hiện cuộc gọi Ajax và / hoặc người dùng chỉ cần đóng trang hoặc trình duyệt, thì điều này có thể giết chết mọi liên lạc một cách bất ngờ. Về cơ bản, bạn sẽ thấy lỗi này bất cứ khi nào đầu kia chấm dứt ứng dụng của họ và bạn không lường trước được.

Nếu bạn gặp phải Ngoại lệ này trong ứng dụng của mình, thì điều đó có nghĩa là bạn nên kiểm tra mã của mình ở nơi xảy ra IO (Đầu vào / Đầu ra) và bọc nó bằng một khối thử / bắt để bắt IOException này. Sau đó, tùy bạn quyết định cách bạn muốn xử lý tình huống bán hợp lệ này.

Trong trường hợp của bạn, nơi sớm nhất mà bạn vẫn có quyền kiểm soát là cuộc gọi đến HttpMethodDirector.executeWithRetry- Vì vậy, hãy đảm bảo rằng cuộc gọi được gói bằng khối thử / bắt và xử lý nó như thế nào bạn thấy phù hợp.

Tôi đặc biệt khuyên bạn không nên ghi lại các lỗi cụ thể của SocketException-Broken Tube ở bất kỳ mức độ nào khác ngoài mức độ gỡ lỗi / theo dõi. Khác, điều này có thể được sử dụng như một hình thức tấn công của DOS (Từ chối dịch vụ) bằng cách điền vào nhật ký. Hãy thử và làm cứng và kiểm tra tiêu cực ứng dụng của bạn cho kịch bản phổ biến này.


Nó không phải là do các đồng nghiệp đóng kết nối trong khi bạn đang đọc từ nó. Điều đó gây ra một tình trạng khác nhau, cuối luồng, thường không phải là ngoại lệ.
Hầu tước Lorne

5

Tất cả các luồng và kết nối mở cần được đóng đúng cách, vì vậy lần sau khi chúng tôi cố gắng sử dụng đối tượng urlConnection, nó sẽ không gây ra lỗi. Ví dụ, thay đổi mã sau đây đã sửa lỗi cho tôi.

Trước:

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

Sau:

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
Điều này thực sự rất lạ, bởi vì BufferedOutputStreamluôn luôn gọi close()luồng đầu ra bên dưới của nó (do đó trên luồng đầu ra của urlConnection). Xem docs.oracle.com/javase/6/docs/api/java/io/ , và docs.oracle.com/javase/6/docs/api/java/io/. Vì vậy, khi bạn gọi os.close()nó nên đã bị đóng . Không?
phản đối

4
'os.close ()' không phải là 'phải'. Không phải là 'out.close ()' cho vấn đề đó. 'bw.c Đóng ()' là đủ. @obedker là đúng. Thay đổi này một mình không thể khắc phục vấn đề.
Hầu tước Lorne

1

Tôi đã thực hiện chức năng tải xuống dữ liệu thông qua máy chủ FTP và cũng tìm thấy ngoại lệ tương tự trong khi tiếp tục tải xuống. Để giải quyết ngoại lệ này, bạn sẽ luôn phải ngắt kết nối với phiên trước đó và tạo phiên bản mới của Máy khách và kết nối mới với máy chủ. Cách tiếp cận tương tự này cũng có thể hữu ích cho HTTPClient.


Để khắc phục lỗi này, bạn phải tránh sử dụng các ổ cắm kín.
Hầu tước Lorne

3
CƯỜI LỚN. Bạn có thể lãng phí cả ngày để sửa tất cả các lời khuyên không có thật về SO.
Dave

2
@Dave Thật vậy. Đôi khi tôi làm.
Hầu tước Lorne

1

Tôi cũng gặp vấn đề tương tự khi tôi đang phát triển một ứng dụng Java đơn giản nghe trên một TCP cụ thể. Thông thường, tôi không có vấn đề gì, nhưng khi tôi chạy một số bài kiểm tra căng thẳng, tôi nhận thấy rằng một số kết nối bị lỗi socket write exception.

Sau khi điều tra tôi tìm thấy một giải pháp giải quyết vấn đề của tôi. Tôi biết câu hỏi này khá cũ, nhưng tôi thích chia sẻ giải pháp của mình, ai đó có thể thấy nó hữu ích.

Vấn đề là về việc tạo ServerSocket. Tôi đọc từ Javadoc có giới hạn mặc định là 50 ổ cắm đang chờ xử lý. Nếu bạn thử mở một kết nối khác, chúng sẽ bị từ chối. Giải pháp chỉ đơn giản là thay đổi cấu hình mặc định này ở phía máy chủ. Trong trường hợp sau, tôi tạo một máy chủ Socket nghe ở cổng TCP 10_000và chấp nhận tối đa 200các socket đang chờ xử lý.

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

Từ Javadoc của ServerSocket :

Độ dài hàng đợi tối đa cho các chỉ dẫn kết nối đến (yêu cầu kết nối) được đặt thành tham số tồn đọng. Nếu chỉ báo kết nối đến khi hàng đợi đầy, kết nối bị từ chối.


0

Vấn đề có thể là các tệp được triển khai của bạn không được cập nhật với các phương thức RMI chính xác. Kiểm tra xem giao diện RMI của bạn có các tham số cập nhật hay cấu trúc dữ liệu được cập nhật mà máy khách của bạn không có. Hoặc máy khách RMI của bạn không có tham số nào khác với phiên bản máy chủ của bạn.

Đây chỉ là một phỏng đoán có giáo dục. Sau khi triển khai lại các tệp lớp của ứng dụng máy chủ của tôi và kiểm tra lại, vấn đề "Đường ống bị hỏng" đã biến mất.


Có gì RMI phương pháp? RMI không được đề cập trong câu hỏi.
Hầu tước Lorne

0

Các câu trả lời trên minh họa lý do cho việc này java.net.SocketException: Broken pipe: đầu kia đóng kết nối. Tôi muốn chia sẻ kinh nghiệm những gì đã xảy ra khi tôi gặp nó:

  1. trong yêu cầu của khách hàng, Content-Typetiêu đề được đặt nhầm lớn hơn cơ thể yêu cầu thực sự là (thực tế không có cơ thể nào cả)
  2. dịch vụ dưới cùng trong ổ cắm tomcat đang chờ dữ liệu cơ thể có kích thước đó (http trên TCP đảm bảo phân phối bằng cách đóng gói và ...)
  3. khi hết 60 giây, tomcat ném hết thời gian: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception java.net.SocketTimeoutException: null
  4. khách hàng nhận được phản hồi với mã trạng thái 500 vì ngoại lệ hết thời gian.
  5. kết nối chặt chẽ của khách hàng (vì nó nhận được phản hồi).
  6. Tomcat ném java.net.SocketException: Broken pipevì khách hàng đóng nó.

Đôi khi, tomcat không ném ngoại lệ pip bị hỏng, vì ngoại lệ hết thời gian đóng kết nối, tại sao một sự khác biệt như vậy cũng làm tôi bối rối.


Các Content-Typetiêu đề không chỉ định một số, vì vậy nó không thể là 'lớn' hơn bất cứ điều gì. Ngoại lệ thời gian chờ không đóng ổ cắm. Trả lời không có ý nghĩa gì.
Hầu tước Lorne

@EJP có thể đó là độ dài nội dung, đừng nhớ. Tôi nghĩ thời gian chờ trong Tomcat khiến nó trả về 500, trong khi máy khách nhận được phản hồi này và đóng kết nối, điều này cuối cùng khiến tomcat không nhận đủ byte như mong đợi.
Tiina

Nếu Tomcat gửi 500, nó đã nhận được mọi thứ nó dự định. Vẫn không có ý nghĩa.
Hầu tước Lorne

@ user207421 không thật. Tomcat gửi 500 vì nó không nhận được tất cả những gì nó mong đợi, nhưng đã hết thời gian.
Tiina

-1

JavaDoc:

Độ dài hàng đợi tối đa cho các chỉ báo kết nối đến (yêu cầu kết nối) được đặt thành 50. Nếu chỉ báo kết nối đến khi hàng đợi đầy, kết nối bị từ chối.

Bạn nên tăng tham số "tồn đọng" của ServerSocket của bạn, ví dụ

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

1
Tăng lượng tồn đọng sẽ không giải quyết được lỗi 'đường ống bị hỏng'.
Hầu tước Lorne

1
nhưng nó giúp ích cho tôi
Thứ

@EJP Tôi đã thử nghiệm một máy chủ trên Linux và nó đã hoạt động, nhưng trên Mac OS - thì không. Và tăng kích thước tồn đọng đã giúp tôi. Tôi không biết rằng kích thước tối đa là 50.
Thứ

1
Bạn đã thử nghiệm một cái gì đó khác. Nghe tồn đọng không có gì để làm với ngoại lệ này. Nó giúp kết nối từ chối hoặc hết giờ tại máy khách. Không ngoại lệ 'ổ cắm đóng', đó là một lỗi cục bộ.
Hầu tước Lorne

Đối với nhóm ổ cắm (chẳng hạn như máy chủ HTTP như Tomcat), có các cài đặt cấu hình cho số lượng "chấp nhận" đang chờ xử lý, máy chủ sẽ "xếp hàng" trước khi gửi các luồng phục vụ và một cài đặt riêng cho số lượng luồng trong nhóm. Tuy nhiên, không có gì trong số này liên quan đến vấn đề sau khi Máy chủ "chấp nhận ()" khi kết nối được thiết lập - luồng đầu ra đột ngột (vô tình) "đóng".
Darrell Teague

-1

Nguyên nhân là do các đồng nghiệp từ xa đóng ổ cắm của nó (ví dụ như sự cố), vì vậy nếu bạn cố gắng ghi dữ liệu cho anh ta chẳng hạn, bạn sẽ nhận được điều này. để giải quyết vấn đề đó, bảo vệ đọc / ghi vào các hoạt động của ổ cắm giữa (thử bắt), sau đó quản lý trường hợp mất kết nối vào bẫy (gia hạn kết nối, dừng chương trình, ... vv):

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

3
Điều này sẽ ghi lại các vấn đề, nhưng nó sẽ không giải quyết chúng. Để giải quyết chúng, bạn phải (a) giải quyết mọi lỗi giao thức ứng dụng có thể và (b) đóng các kết nối chết. Không chỉ đăng nhập ngoại lệ, hầu hết trong số đó là gây tử vong.
Hầu tước Lorne

@EJP: tất nhiên, khi bạn gặp lỗi, bạn nên dọn sạch ổ cắm (ngữ cảnh) của mình vào câu lệnh bắt. Tôi không thể đoán những gì nhà phát triển sẽ làm. đó là của riêng mình để làm thủ tục sạch sẽ
elhadi dp ıpɐɥ ן

Tùy thuộc vào nhà phát triển để khắc phục sự cố giao thức ứng dụng gây ra lỗi và có. Không có gì tôi câu trả lời của bạn hoặc mã giúp anh ta. làm như vậy. 'Chèn các hoạt động đọc / ghi giữa' không khắc phục được. Câu trả lời của bạn không chính xác.
Hầu tước Lorne

@ user207421, nhà phát triển hỏi cách sửa đường ống bị hỏng. tôi đã chỉ định cho anh ta để bảo vệ đầu tiên chương trình của anh ta bằng cách sử dụng (thử bắt). Điều này sẽ tránh chương trình bị sập. sau đó tôi chèn một bình luận để chỉ cho anh ta làm sạch. Nói chung trong một lỗi đường ống bị hỏng, có nhiều lựa chọn thay thế, tôi không thể đoán được mục tiêu của chương trình, dừng chương trình, gia hạn kết nối, dữ liệu có bị hỏng hay không .... vv. Lập trình viên quyết định lựa chọn nào để làm? Có gì sai với câu trả lời của tôi?
elhadi dp ıpɐɥ ן
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.