如何修复 java.net. SocketException: 管道破裂?

我使用 apache commons http client 调用 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)

是否有人能够建议是什么导致了这个异常以及如何调试它?

603350 次浏览

这是由以下原因造成的:

  • 最常见的情况是,在另一端已经关闭连接时写入该连接;
  • 不太常见的情况是,对等端关闭连接而不读取其端已挂起的所有数据。

因此,在这两种情况下,都存在定义不良或实现不良的应用程序协议。

还有第三个原因,我不会在这里记录,但涉及到对等采取慎重的行动,以重置,而不是正确地关闭连接。

我已经通过 FTP 服务器实现了数据下载功能,在恢复下载时也发现了同样的异常。 要解决此异常,您必须始终断开与前一个会话的连接,并创建 Client 的新实例和与服务器的新连接。同样的方法对 HTTPClient 也有帮助。

在我们的例子中,我们在应用服务器上执行负载测试时遇到了这种情况。问题是我们需要向 JVM 添加额外的内存,因为它已经用完了。这解决了问题。

尝试增加 JVM 可用的内存,或者在出现这些错误时监视内存使用情况。

所有打开的流和连接都需要正确关闭,所以下次我们尝试使用 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.

JavaDoc:

传入连接指示符的最大队列长度(a 请求连接)设置为50。如果连接指示到达 当队列已满时,将拒绝连接。

例如,您应该增加 ServerSocket 的“ backlog”参数

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

问题可能是您部署的文件没有使用正确的 RMI 方法进行更新。检查您的 RMI 接口是否具有客户端没有的更新参数或更新的数据结构。或者您的 RMI 客户端没有与服务器版本不同的参数。

这只是一个有根据的猜测。在重新部署服务器应用程序的类文件并重新测试之后,“断管”问题就消失了。

上面的答案说明了这个 java.net.SocketException: Broken pipe的原因: 另一端关闭了连接。我想分享一下我遇到这种情况时的经历:

  1. 在客户端的请求中,Content-Type头被错误地设置为比请求主体实际大(实际上根本没有主体)
  2. Tomcat 套接字中的底层服务正在等待那个大小的主体数据(http 在 TCP 上,它通过封装和... 来确保传递)
  3. 当60秒过期时,Tomcat 抛出时间异常: Service ()用于 servlet [ patterServlet ]的上下文,路径[]抛出异常 例外: null
  4. 由于超时异常,客户端收到状态代码为500的响应。
  5. 客户端关闭连接(因为它接收响应)。
  6. Tomcat 抛出 java.net.SocketException: Broken pipe,因为客户端关闭了它。

有时候,Tomcat 并没有抛出断点异常,因为超时异常关闭了连接,为什么这样的差异也让我困惑。

SocketException: 管道中断,是由于“另一端”(客户端或服务器)在您的代码读取或写入连接时关闭连接造成的。

在客户机/服务器应用程序中,这是一个非常常见的异常,它接收来自应用程序控件之外的客户机或服务器的流量。例如,客户端是一个浏览器。如果浏览器发出 Ajax 调用,并且/或者用户只是关闭页面或浏览器,那么这可以有效地意外终止所有通信。基本上,只要对方终止应用程序,您就会看到这个错误,而您并没有预料到这一点。

如果您在应用程序中遇到此异常,那么这意味着您应该检查 IO (Input/Output)发生的代码,并用 try/catch 块包装它,以捕捉此 IOException。然后,由你来决定如何处理这种半有效的情况。

在您的例子中,您仍然拥有控制权的最早位置是对 HttpMethodDirector.executeWithRetry的调用-所以确保调用包含 try/catch 块,并按照您认为合适的方式处理它。

我强烈建议不要在除了调试/跟踪级别以外的任何情况下记录 SocketException-Breakipe 特定错误。否则,这可以作为一种形式的 DOS (分布式拒绝服务攻击)攻击,通过填写日志。尝试对应用程序进行这种常见场景的硬化和负面测试。

在开发一个侦听特定 TCP 的简单 Java 应用程序时,我遇到了同样的问题。通常,我没有问题,但是当我运行一些压力测试时,我注意到一些连接因为错误 socket write exception而中断。

经过调查,我找到了解决问题的办法。我知道这个问题很古老,但我更喜欢分享我的解决方案,有人会发现它很有用。

问题出在 ServerSocket 的创建上。我从 Javadoc 上读到,默认限制是50个挂起的套接字。如果您尝试打开另一个连接,这些将被拒绝。解决方案只需在服务器端更改此默认配置即可。在下面的示例中,我创建了一个 Socket 服务器,它在 TCP 端口 10_000侦听并接受 max 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:

传入连接指示(连接请求)的最大队列长度设置为 backlog 参数。如果连接指示在队列已满时到达,则拒绝连接。

我注意到我在创建时使用了不正确的 HTTP 请求 URL,后来我改变了它,它解决了我的问题。我的上传网址是: http://192.168.0.31:5000/uploader,而我使用的是 http://192.168.0.31:5000。那是个接警电话。还有一个 java.net。管道破裂?例外。

这是我的理由。可能会导致你检查一点,当这个问题

  public void postRequest()  {




Security.insertProviderAt(Conscrypt.newProvider(), 1);


System.out.println("mediafilename-->>" + mediaFileName);
String[] dirarray = mediaFileName.split("/");
String file_name = dirarray[6];
//Thread.sleep(10000);


RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file",file_name, RequestBody.create(MediaType.parse("video/mp4"), new File(mediaFileName))).build();
OkHttpClient okHttpClient = new OkHttpClient();
//        ExecutorService executor = newFixedThreadPool(20);
//        Request request = new Request.Builder().post(requestBody).url("https://192.168.0.31:5000/uploader").build();
Request request = new Request.Builder().url("http://192.168.0.31:5000/uploader").post(requestBody).build();
//        Request request = new Request.Builder().url("http://192.168.0.31:5000").build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {


//


call.cancel();


runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(),"Something went wrong:" + " ", Toast.LENGTH_SHORT).show();


}
});
e.printStackTrace();


}


@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {




runOnUiThread(new Runnable() {
@Override
public void run() {


try {
System.out.println(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
});




System.out.println("Response ; " + response.body().toString());
//                Toast.makeText(getApplicationContext(), response.body().toString(),Toast.LENGTH_LONG).show();
System.out.println(response);


}
});