如何修复 java.net.SocketException:损坏的管道?
Posted
技术标签:
【中文标题】如何修复 java.net.SocketException:损坏的管道?【英文标题】:How to fix java.net.SocketException: Broken pipe? 【发布时间】:2011-01-19 13:19:29 【问题描述】:我正在使用 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)
有人可以建议导致此异常的原因以及如何调试它吗?
【问题讨论】:
这个最好试试***.com/questions/10142409/… 【参考方案1】:这是由于:
最常见的情况是,在另一端已经关闭连接时写入连接; 通常情况下,对等方关闭连接而不读取其端已经挂起的所有数据。因此,在这两种情况下,您的应用程序协议定义或实现都很差。
还有第三个原因,我不会在这里记录,但它涉及对等方故意采取行动来重置而不是正确关闭连接。
【讨论】:
我已经确认了原因。您正在写作,而另一端已经关闭了连接。解决方法是不要那样做。由于另一端没有阅读它,所以无论如何都没有任何意义。正如我也说过的,如果发生这种情况,则说明您的应用程序协议规范或实现有问题,很可能您甚至没有。 如果服务器端 HTTP 应用程序出现 Broken Pipe 异常,这仅意味着客户端浏览器已退出/转到另一个页面/超时/返回历史记录/无论如何。忘了它吧。 我们在浏览器中中止 ajax 请求时看到了这个异常(使用XMLHttpRequest.abort()
)。
一个可能的原因是代理或 Http 服务器过早关闭连接。例如,您的 J2EE 服务器和应用程序客户端之间的 Apache Http 服务器,具有短定义的超时。至少这是在我们的生产环境中发生的事情。客户抱怨页面未完全加载,我们在 JBoss 日志中看到此错误。经过一些测试,我们注意到问题在于配置不当的 Http Server。
这是一个糟糕的答案,因为 OP 要求同时提供“原因”和“如何调试”......无论如何,因为它不一定(并且可以说很少)与“a定义或实现不明确的应用程序协议”。如前所述,这是因为套接字的侦听(服务器)端不会“通知”客户端连接已关闭,并且(例如在防火墙的情况下)从客户端的角度来看甚至可能不知道它已关闭。至于调试,知道它关闭的方法是尝试写入,至于原因,必须通过网络套接字链(代理等)追踪到服务器。【参考方案2】:
在我们的案例中,我们在应用服务器上执行负载测试时遇到了这种情况。事实证明,我们需要向 JVM 添加额外的内存,因为它已经用完了。这解决了问题。
尝试增加 JVM 可用的内存,或者在出现这些错误时监控内存使用情况。
【讨论】:
我在负载测试期间遇到了同样的问题。我猜数据需要很长时间才能生成,并且负载测试工具没有等待数据足够长的时间然后关闭连接。在您的情况下,添加内存可能会减少数据生成过程的持续时间,因此负载测试可以获取所有数据而没有超时限制。增加内存的另一种方法是增加负载测试工具的超时时间。 显然内存不足导致应用程序关闭接收套接字或完全退出,效果相同。低内存本身不会导致管道损坏。 这实际上在我们的重负载应用程序中似乎也是一个问题【参考方案3】:SocketException: Broken pipe,是由“另一端”(客户端或服务器)在您的代码读取或写入连接时关闭连接引起的。
这是客户端/服务器应用程序中的一个非常常见的异常,这些应用程序从应用程序控制之外的客户端或服务器接收流量。例如,客户端是浏览器。如果浏览器进行了 Ajax 调用,和/或用户只是关闭了页面或浏览器,那么这可能会意外地终止所有通信。基本上,只要另一端终止他们的应用程序,您就会看到这个错误,而您没有预料到。
如果您在应用程序中遇到此异常,则意味着您应该检查发生 IO(输入/输出)的代码,并使用 try/catch 块对其进行包装以捕获此 IOException。然后,由您决定如何处理这种半有效的情况。
在您的情况下,您仍然可以控制的最早位置是对 HttpMethodDirector.executeWithRetry
的调用 - 因此,请确保使用 try/catch 块包装该调用,并按照您认为合适的方式处理它。
我强烈建议不要在调试/跟踪级别以外的任何地方记录 SocketException-Broken Pipe 特定错误。否则,这可以通过填充日志被用作 DOS(拒绝服务)攻击的一种形式。针对这种常见情况,尝试强化您的应用程序并对其进行负面测试。
【讨论】:
这不是由对等端在您读取连接时关闭连接引起的。这会导致不同的流结束条件,这通常根本不是例外。【参考方案4】:所有打开的流和连接都需要正确关闭,所以下次我们尝试使用 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.
【讨论】:
这实际上很奇怪,因为BufferedOutputStream
总是在其底层输出流上调用close()
(因此在urlConnection
的输出流上)。见docs.oracle.com/javase/6/docs/api/java/io/… 和docs.oracle.com/javase/6/docs/api/java/io/… 所以当你打电话给os.close()
它应该已经关闭了。没有?
'os.close()' 不是“必须的”。 'out.close()' 也不是。 'bw.close()' 就足够了。 @obedker 是正确的。仅此更改无法解决问题。【参考方案5】:
我已经通过 FTP 服务器实现了数据下载功能,并且在恢复下载时也发现了同样的异常。 要解决此异常,您将始终必须断开与先前会话的连接并创建客户端的新实例以及与服务器的新连接。同样的方法也可能对 HTTPClient 有所帮助。
【讨论】:
要解决此错误,您必须避免尝试使用已关闭的套接字。 哈哈。您可能会浪费一整天的时间来纠正所有关于 SO 的虚假建议。 @Dave 确实。有时我会这样做。【参考方案6】:我在开发一个侦听特定 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 参数。如果队列满时有连接指示到达,则拒绝连接。
【讨论】:
这一切都与连接重置无关。如果你有一个客户端套接字可以写入,那么根据定义,你没有遇到监听积压限制。【参考方案7】:问题可能是您部署的文件未使用正确的 RMI 方法进行更新。检查您的 RMI 接口是否已更新参数,或更新您的客户端没有的数据结构。或者您的 RMI 客户端没有与您的服务器版本不同的参数。
这只是一个有根据的猜测。重新部署我的服务器应用程序的类文件并重新测试后,“Broken pipe”的问题就消失了。
【讨论】:
什么 RMI方法?问题中没有提到 RMI。【参考方案8】:上面的答案说明了这个java.net.SocketException: Broken pipe
的原因:另一端关闭了连接。我想分享一下我遇到它时发生的事情:
-
在客户端的请求中,
Content-Type
标头被错误地设置为大于实际请求正文(实际上根本没有正文)
tomcat 套接字中的底层服务正在等待该大小的主体数据(http 位于 TCP 上,通过封装和...确保交付)
当60秒过期时,tomcat抛出超时异常:
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
java.net.SocketTimeoutException: null
由于超时异常,客户端收到状态码为 500 的响应。
客户端关闭连接(因为它收到响应)。
tomcat 抛出 java.net.SocketException: Broken pipe
因为客户端关闭了它。
有时,tomcat 不会抛出损坏的 pip 异常,因为超时异常关闭了连接,为什么这样的差异也让我感到困惑。
【讨论】:
Content-Type
标头未指定数字,因此它不能“大于”任何值。超时异常不会关闭套接字。答案毫无意义。
@EJP 可能是内容长度,不记得了。我认为Tomcat中的超时使其返回500,而客户端收到此响应并关闭连接,最终导致tomcat没有收到预期的足够字节。
如果 Tomcat 发送 500,它已经收到了它计划发送的所有内容。仍然没有意义。
@user207421 不是真的。 Tomcat 发送 500 是因为它没有收到它所期望的所有内容,但是超时了。【参考方案9】:
我注意到我在制作它时使用了不正确的 HTTP 请求 URL,后来我更改它解决了我的问题。我的上传网址是:http://192.168.0.31:5000/uploader,而我使用的是http://192.168.0.31:5000。那是一个接听电话。并得到一个 java.net.SocketException: Broken pipe?例外。
这就是我的理由。出现此问题时可能会导致您再检查一点
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);
);
【讨论】:
【参考方案10】:JavaDoc:
传入连接指示的最大队列长度(a 连接请求)设置为 50。如果连接指示到达 当队列已满时,连接被拒绝。
你应该增加你的ServerSocket的“backlog”参数,例如
int backlogSize = 50 ;
new ServerSocket(port, backlogSize);
【讨论】:
增加 backlog 并不能解决“broken pipe”错误。 但这对我有帮助 @EJP 我在 Linux 上测试了一个服务器,它可以工作,但在 Mac OS 上却不行。并且增加积压的大小帮助了我。我不知道最大尺寸是 50。 你测试了别的东西。 Listen backlog 与此异常无关。它有助于客户端的连接拒绝或超时。不是“套接字关闭”异常,这是一个本地错误。 对于套接字池(例如像 Tomcat 这样的 HTTP 服务器),在调度服务线程之前,服务器将“排队”的挂起“接受”的数量有配置设置,另外还有一个单独的设置为池中的线程。但是,这与建立连接时在服务器“accept()”之后的问题无关 - 输出流突然(不知不觉地)“关闭”。以上是关于如何修复 java.net.SocketException:损坏的管道?的主要内容,如果未能解决你的问题,请参考以下文章