捕获异常时如何优雅地清理 HTTPURLConnection?

发布于 2025-01-01 09:22:51 字数 2158 浏览 1 评论 0原文

我有一个 Java 小程序,它使用 HTTPURLConnection 类将非常大的文件上传到 IIS-7 Web 服务器。该小程序将文件分成块,然后使用固定长度流将这些块 POST 到 PHP 脚本。

有时,在上传文件块时,客户端和服务器之间的网络连接会神秘地断开。发生这种情况时,我对 writeBytes() 方法的调用会抛出一个我捕获的 IOException 。捕获此异常后,我进入 finally 块,尝试清理内容。由于写入连接的数据不足(请记住,这是固定长度流),尝试关闭输出流也会失败。结果,连接看起来“一直存在”(即底层套接字保持打开状态)。这似乎可以通过查看 StreamingOutputStream 类的 close() 方法的源代码(请注意说明套接字无法关闭的注释)。

问题:

在写入 HTTPURLConnection 时捕获 IOException 后,是否有一种优雅的方式可以关闭?将 HTTPURLConnection 对象告知 disconnect() 似乎还不够好。

附加信息:

这是我在网络被堵塞时看到的第一个异常,是由我调用 HTTPURLConnection 类的 writeBytes() 方法引发的:

java.io.IOException: Error writing request body to server
    at sun.net.www.protocol.http.HttpURLConnection$StreamingOutputStream.checkError(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection$StreamingOutputStream.write(Unknown Source)
    at java.io.DataOutputStream.write(Unknown Source)
    at MultiPartPostThread.run(MultiPartPostThread.java:321)

我捕获了这个异常,并且然后尝试关闭我用来写入连接的 DataOutputStream 对象。当我这样做时,我得到了这个异常:

java.io.IOException: insufficient data written
    at sun.net.www.protocol.http.HttpURLConnection$StreamingOutputStream.close(Unknown Source)
    at java.io.FilterOutputStream.close(Unknown Source)
    at MultiPartPostThread.run(MultiPartPostThread.java:370)

我可以通过注意到故障条目需要 10 分钟才能显示在 IIS 日志中来验证套接字是否保持打开状态。该延迟(10 分钟)恰好是我的连接超时值。 IIS 日志中的示例行(为了简洁而进行了修剪):

2012-01-31 20:20:07 POST /upload_handler.php 200 0 0 356 26215490 3666
2012-01-31 20:20:10 POST /upload_handler.php 200 0 0 356 26215490 3853
2012-01-31 20:30:22 POST /upload_handler.php 500 0 64 0 15286099 611442

请注意,在日志的前两行中,我们传输得很好:每次发送 25 MB,并返回 200 状态代码。最后一次成功传输后 10 分钟就会出现故障,并显示无用的错误 500(请注意,这只是部分传输;仅传输了约 15MB)。实际上,这种神秘的断开连接发生在整个过程大约 1 分钟后,因此我在 IIS 跟踪日志中看到的超时消息是转移注意力的信息。我在 PHP 日志、HTTPERR 日志或服务器上的系统日志中没有看到任何有用的信息。

I have a Java applet that uses the HTTPURLConnection class to upload very large files to an IIS-7 web-server. The applet chunks the files into pieces, then POSTs the pieces using fixed-length streaming to a PHP script.

Occasionally, when uploading file chunks, the network connection between the client and the server mysteriously drops. When this happens, my call to the writeBytes() method throws an IOException which I catch. After catching this exception, I drop down into my finally block where I try to clean things up. Because insufficient data was written to the connection (remember, this is fixed-length streaming), the attempt to close the output stream also fails. As a result, the connection seemingly "sticks around" (i.e. the underlying socket remains open). This seems to be verified by looking at the source code to the close() method for the StreamingOutputStream class (note the comment stating that the socket cannot be closed).

Question:

Is there a graceful way I can shut down after catching an IOException while writing out to an HTTPURLConnection? Telling the HTTPURLConnection object to disconnect() doesn't seem to be good enough.

Additional info:

Here's the first exception I see when the network gets hosed, thrown by my call to the writeBytes() method of the HTTPURLConnection class:

java.io.IOException: Error writing request body to server
    at sun.net.www.protocol.http.HttpURLConnection$StreamingOutputStream.checkError(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection$StreamingOutputStream.write(Unknown Source)
    at java.io.DataOutputStream.write(Unknown Source)
    at MultiPartPostThread.run(MultiPartPostThread.java:321)

I catch this exception, and then attempt to close the DataOutputStream object that I was using to write to the connection. When I do so, I get this exception:

java.io.IOException: insufficient data written
    at sun.net.www.protocol.http.HttpURLConnection$StreamingOutputStream.close(Unknown Source)
    at java.io.FilterOutputStream.close(Unknown Source)
    at MultiPartPostThread.run(MultiPartPostThread.java:370)

I can verify that the socket is staying open, by noting that it takes 10 minutes for the failure entry to show up in the IIS log. That delay (10 minutes) happens to be my connection timeout value. Example lines from the IIS log (trimmed for brevity):

2012-01-31 20:20:07 POST /upload_handler.php 200 0 0 356 26215490 3666
2012-01-31 20:20:10 POST /upload_handler.php 200 0 0 356 26215490 3853
2012-01-31 20:30:22 POST /upload_handler.php 500 0 64 0 15286099 611442

Note that in the first two lines of the log we're transferring along nicely: 25 MB is sent each time, and a 200 status code is returned. The failure then shows up 10 minutes after the last successful transfer, with an unhelpful error 500 (and note that this was only a partial transfer; only ~15MB transferred). In actuality, this mysterious disconnect occurs about 1 minute into the whole procedure, so the timeout messages I'm seeing in my IIS trace logs are red herrings. I see nothing helpful in my PHP logs, the HTTPERR log, or in the system log on the server.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

耳根太软 2025-01-08 09:22:51

使用的资源

HTTPURLConnection 

在流上调用 close() 应该释放HTTPURLConnection javadoc

。在请求之后调用 HttpURLConnection 的 InputStream 或 OutputStream 上的 close() 方法可能会释放与此关联的网络资源实例,但对任何共享持久连接没有影响。如果持久连接当时处于空闲状态,则调用disconnect() 方法可能会关闭底层套接字。

Calling close() on your stream should release the resources used by

HTTPURLConnection 

Here is note from HTTPURLConnection javadoc

Calling the close() methods on the InputStream or OutputStream of an HttpURLConnection after a request may free network resources associated with this instance but has no effect on any shared persistent connection. Calling the disconnect() method may close the underlying socket if a persistent connection is otherwise idle at that time.

若有似无的小暗淡 2025-01-08 09:22:51

我越深入地研究这个问题,就越确信无法进行优雅的清理。 disconnect() 调用似乎不足以关闭底层套接字;您只需等待它超时。

我想我会尝试使用 Apache HttpComponents HttpClient (如底部推荐的这个太棒了wiki 帖子),看看它的内置错误处理是否可以处理我所看到的这种奇怪的情况。

The more I look into this, the more I'm convinced that a graceful cleanup cannot take place. A disconnect() call doesn't seem to be enough to close the underlying socket; you simply have to wait for it to time out.

I think I'll try using the Apache HttpComponents HttpClient (as recommended at the bottom of this terrific SO wiki post), to see if its built-in error handling can take care of this bizarre case I'm seeing.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文