如何修复java.net.SocketException:管道损坏?


150

我正在使用apache commons http客户端使用post方法调用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)

有人可以建议导致此异常的原因以及如何对其进行调试吗?


Answers:


81

原因是:

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

因此,在两种情况下,您的应用程序协议定义或实施都不充分。

还有第三个原因,我在这里不做记录,但是涉及到对等方采取故意的操作来重置而不是正确地关闭连接。


4
您能帮我找出并更正吗?基本上,如何确定原因并解决?

4
已经确认了原因。另一端已经关闭连接时,您正在写。解决方法是不这样做。由于另一端没有阅读它,因此毫无意义。我也说过,如果发生这种情况,则您的应用程序协议规范或实现存在问题,很可能您甚至没有。
罗恩侯爵

26
如果服务器端HTTP应用程序出现“管道中断”异常,则仅表示客户端浏览器已退出/已转到另一个页面/已超时/已回到历史记录/无论如何。忘掉它。
罗恩侯爵

3
在中止浏览器中的ajax请求(使用XMLHttpRequest.abort())时,我们已经看到了此异常。
obecker 2013年

2
可能的原因是代理或Http服务器过早关闭连接。例如,J2EE服务器和应用程序客户端之间的Apache Http服务器,定义的超时很短。至少那是我们生产环境中发生的事情。客户抱怨页面未完全加载,我们在JBoss日志中看到此错误。经过一些测试,我们注意到问题是配置不正确的Http Server。
里卡多·维拉

32

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

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


4
在负载测试期间,我遇到了同样的问题。我猜想数据会花费很多时间,并且负载测试工具没有等待足够长的时间,然后关闭了连接。在您的情况下,添加内存可能会减少数据生成过程的持续时间,因此负载测试会获得所有数据而没有超时限制。增加内存的另一种方法是增加负载测试工具的超时时间。
朱利安·克朗格

2
显然,内存不足导致应用程序关闭接收套接字或实际上完全退出,具有相同的效果。内存不足本身不会导致管道中断。
罗恩侯爵,2015年

1
这实际上在我们的高负荷应用程序中也似乎是一个问题
Ruben de Vries

7

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

在从应用程序控件外部的客户端或服务器接收流量的客户端/服务器应用程序中,这是一个非常常见的例外。例如,客户端是浏览器。如果浏览器进行了Ajax调用,并且/或者用户只是关闭了页面或浏览器,那么这可以有效地终止所有通信。基本上,只要另一端终止他们的应用程序,而且您没有预料到它,您都会看到此错误。

如果您在应用程序中遇到此Exception,则意味着您应该检查IO(输入/输出)出现的代码,并用try / catch块包装它以捕获此IOException。然后由您决定如何处理这种半有效情况。

在您的情况下,您仍然可以控制的最早位置是对HttpMethodDirector.executeWithRetry- 的调用-因此,请确保使用try / catch块包装该调用,并按照您认为合适的方式进行处理。

我强烈建议不要在调试/跟踪级别以外的任何其他日志记录SocketException-Broken Pipe特定错误。另外,可以通过填充日志来将其用作DOS(拒绝服务)攻击的一种形式。在这种常见情况下,尝试对应用程序进行强化和负面测试。


这不是由对等方在您读取连接时关闭连接引起的。这会导致不同的流结束条件,这通常根本不是例外。
罗恩侯爵,

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
实际上,这很奇怪,因为a BufferedOutputStream总是调用close()其基础输出流(因此调用的输出流urlConnection)。请参阅docs.oracle.com/javase/6/docs/api/java/io/…docs.oracle.com/javase/6/docs/api/java/io/…因此,当您调用os.close()它时,它应该已经关闭了。没有?
obecker 2013年

4
'os.close()'不是'必须'。“ out.close()”都不是问题。'bw.close()'就足够了。@obedker是正确的。仅此更改不能解决问题。
罗恩侯爵

1

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


要解决此错误,您必须避免尝试使用封闭的套接字。
罗恩侯爵,

3
大声笑。您可能会浪费整天的时间来纠正所有关于SO的虚假建议。
戴夫

2
@戴夫确实。有时候我会。
罗恩侯爵,

1

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

经过调查,我找到了解决我的问题的解决方案。我知道这个问题已经很久了,但是我更愿意分享我的解决方案,有人会发现它很有用。

问题出在ServerSocket创建上。我从Javadoc中读取,默认限制为50个待处理的套接字。如果您尝试打开另一个连接,这些将被拒绝。解决方案仅在于在服务器端更改此默认配置。在以下情况下,我创建了一个侦听TCP端口10_000并接受最大200挂起套接字的Socket服务器。

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参数。如果在队列已满时出现连接指示,则拒绝连接。


0

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

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


什么是 RMI方法?问题中未提及RMI。
罗恩侯爵

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. Tomcat抛出异常,java.net.SocketException: Broken pipe因为客户端将其关闭。

有时候,tomcat不会抛出损坏的pip异常,因为超时异常会关闭连接,为什么这样的差异也让我感到困惑。


Content-Type头不指定一个数字,所以它不可能是比什么“大”。超时异常不会关闭套接字。答案毫无意义。
罗恩侯爵

@EJP可能是内容长度,不记得了。我认为Tomcat中的超时使它返回500,而客户端收到此响应并关闭连接,这最终导致tomcat收到的字节数不足,与预期的一样。
Tiina

如果Tomcat发送500,它已经收到了计划的一切。仍然没有道理。
罗恩侯爵,

@ user207421不是真的。Tomcat发送500,因为它没有收到预期的所有信息,但是超时了。
蒂娜(Tiina)

-1

JavaDoc:

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

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

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

1
增加积压无法解决“断线”错误。
罗恩侯爵,

1
但这对我有帮助
TheSecond 16'Feb

@EJP我在Linux上测试了服务器,并且该服务器可以运行,但在Mac OS上却无法运行。积压数量的增加对我有所帮助。我不知道最大大小为
50。– TheSecond

1
您测试了其他内容。监听积压与此异常无关。它有助于客户端的连接拒绝或超时。不是“套接字关闭”异常,这是本地错误。
罗恩侯爵,

对于套接字池(例如像Tomcat这样的HTTP服务器),有一些服务器在分配服务线程之前将“排队”的挂起“接受”数量的配置设置,以及池中线程数量的单独设置。但是,这都与服务器建立连接 “ accept()”之后的输出流突然(不经意地)“关闭” 的问题无关。
Darrell Teague

-1

原因是远程对等方关闭了其套接字(例如崩溃),因此,例如,如果您尝试向其写入数据,您将得到此信息。为了解决这个问题,保护(尝试捕获)之间的套接字操作的读/写,然后将丢失的连接情况管理到捕获中(更新连接,停止程序等):

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

3
这将记录问题,但它不会解决它们。为了解决它们,您必须(a)解决任何可能的应用程序协议错误,并且(b)关闭无效连接。不只是日志异常,其中大多数都是致命的。
罗恩侯爵

@EJP:当然,当您捕获到错误时,应该将套接字(上下文)清理到catch语句中。我无法猜测开发人员将要做什么。清洁程序完全由他自己决定
elhadi dpıpɐɥןǝ18年

开发人员应负责纠正导致错误的应用程序协议问题。您的答案或代码对我没有任何帮助。这样做。“插入之间的读/写操作”不能解决问题。您的答案不正确。
罗恩侯爵,

@ user207421,开发人员询问如何修复损坏的管道。我已指示他首先通过使用(尝试捕获)来保护他的程序。这样可以避免程序崩溃。然后我插入一条评论,指示他进行清洁处理。通常在管道破裂的错误中,有很多选择,我无法猜出程序的目标,停止程序,更新连接,数据是否损坏……等等。由程序员决定要选择哪个选项?我的答案出了什么问题?
elhadi dpıpɐɥןǝ19年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.