HttpUrlConnection.openConnection第二次失败

32
我知道这个问题应该可以通过在打开连接之前使用System.setProperty("http.keepAlive", "false"); 来解决,但对我没有用。第一次尝试这段代码时可以工作,第二次失败了。即使在不到5秒的时间内再次尝试此请求,它也可以工作。如果我等待超过5秒,它又会失败。
这是我的代码:
    System.setProperty("http.keepAlive", "false");
  HttpURLConnection conn = (HttpURLConnection) mURL.openConnection();
  conn.setUseCaches(false); 
  conn.setRequestProperty("Connection","Keep-Alive"); 
  conn.setRequestProperty("User-Agent", useragent);
  conn.setConnectTimeout (30000) ; 
  conn.setDoOutput(true); 
        conn.setDoInput(true); 

  consumer.sign(conn);
  InputSource is = new InputSource(conn.getInputStream());

我在最后一行收到异常:

java.io.IOException: Write error: I/O error during system call, Broken pipe
W/System.err( 2164):  at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.nativewrite(Native Method)
W/System.err( 2164):  at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.access$600(OpenSSLSocketImpl.java:55)
W/System.err( 2164):  at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl$SSLOutputStream.write(OpenSSLSocketImpl.java:583)
W/System.err( 2164):  at java.io.OutputStream.write(OutputStream.java:82)
W/System.err( 2164):  at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.sendRequest(HttpURLConnectionImpl.java:1332)
W/System.err( 2164):  at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.doRequestInternal(HttpURLConnectionImpl.java:1656)
W/System.err( 2164):  at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.doRequest(HttpURLConnectionImpl.java:1649)
W/System.err( 2164):  at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:1153)
W/System.err( 2164):  at org.apache.harmony.luni.internal.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:253)

有人知道这里出了什么问题吗?谢谢!

6个回答

29
HttpURLConnection 在保持连接时使用的连接池存在问题,导致它试图使用已被服务器关闭的连接。默认情况下,Android 对所有连接设置了 KeepAlive。 System.setProperty("http.keepAlive", "false"); 是一种解决方法,可以禁用所有连接的 KeepAlive,从而避免连接池中的错误。 conn.setRequestProperty("Connection","Keep-Alive"); 可以为该特定连接打开 KeepAlive,本质上是撤消 System.setProperty("http.keepAlive", "false"); 的作用。
此外,我总是显式地调用 connect() 方法,这使得你清楚地知道你在结束连接设置。我不确定是否有必要调用此方法。
System.setProperty("http.keepAlive", "false");
HttpURLConnection conn = (HttpURLConnection) mURL.openConnection();
conn.setUseCaches(false); 
conn.setRequestProperty("User-Agent", useragent);
conn.setConnectTimeout(30000);
conn.setDoOutput(true); 
conn.setDoInput(true); 
consumer.sign(conn);

conn.connect();

InputSource is = new InputSource(conn.getInputStream());

谢谢@fonetik提供的答案和解释!由于我已经转向使用HttpClient,所以现在无法测试它,但我相信这可以帮助其他人。再次感谢! - ggomeze
@user464773 System.setProperty(“ http.keepAlive”,“ false”); 禁用应用程序所有连接上的_KEEP ALIVE_。这正是我们想要的,因为_KEEP ALIVE_总会导致错误。仅使用conn.setRequestProperty(“connection”,“close”); 会为conn禁用_KEEP ALIVE_,留下漏洞,当我们忘记将其添加到任何一个连接中时,该漏洞就会被打开。 我不知道这个漏洞是否已回到Harmony,但Android团队已经意识到了它:http://code.google.com/p/android/issues/detail?id=7786 - barry
1
我想知道这个答案是否仍然可接受/最佳,或者是否有更好的建议来解决“javax.net.ssl.SSLException: Write error: ssl=0x130f4a0: I/O error during system call, Broken pipe”出现的情况?十分感谢。 - Ewoks
即使连接挂起,使用setRequestProperty禁用keepalive似乎也不起作用。 - Alkanshel

25

您不需要使用System.setProperty("http.keepAlive", "false");

您所需的是conn.setRequestProperty("connection", "close");

这可以解决问题,但会使保持连接失效,因此可能会导致多个连接速度变慢(这很遗憾)。我在查看Harmony Bug Tracker,但没能找到什么有用信息。

@fonetik,请问您是否知道这个问题是否已经在Harmony上提出?虽然另一个与HTTP相关的Luni缺陷仍未分配处理人员已经超过一个月了,所以这也不大有帮助。


3

我解决了这个问题。在这里我给出了代码,以防对某人有所帮助。基本上我看到谷歌上的趋势是使用HttpClient/HttpGet而不是HttpUrlConnection。所以我尝试使用这些类,并且一切都正常:

final HttpClient client = new DefaultHttpClient();
final HttpGet conn = new HttpGet(mURL.toString());

OAuthConsumer consumer = mOAuthManager.getPostConsumer();
consumer.sign(conn);
HttpResponse response = client.execute(conn);
InputSource is = new InputSource(response.getEntity().getContent());

10
现在谷歌建议使用HttpUrlConnection(从Gingerbread开始),因为已经修复了其中的一些错误: http://android-developers.blogspot.com/2011/09/androids-http-clients.html - Jan Berkel
迟早,特别是随着Android M的增长,HttpClient正在逐渐被弃用。URLConnection建议用于具有长期使用寿命的应用程序。 - Josh

1

这个错误已经在Android2.3版本中被修复了,正如我们所知道的那样,System.setProperty("http.keepAlive", "false"); 不是一个非常好的解决方案,因为在移动设备上,每次创建连接都会产生高成本。


0

当我尝试打开https连接时,第一次是正常的,但第二次失败了,因为我设置了系统属性值而不是HttpsURLConnection连接。第二次打开https连接时,我遇到了java.io.IOException:写入错误:I/O问题。我在我的应用程序中使用了以下代码。

System.setProperty("http.proxyHost", proxy);
System.setProperty("http.proxyPort", port);

但是当我将它改为以下内容时,它就可以正常工作。

javax.net.ssl.HttpsURLConnection ucon = (javax.net.ssl.HttpsURLConnection) urlWPF.openConnection(proxyserver);

ucon.setRequestProperty("http.proxyHost", proxy);
ucon.setRequestProperty("http.proxyPort", port);

如果您设置了系统属性,则该属性将适用于整个应用程序。如果您想要重置相同的属性,则可以采取两种方法。一种方法是您需要进行服务器刷新,第二种方法是您需要更改上述所述的HttpsURLConnection.setRequestProperty在必要时使用。


0

我认为你的问题在于代码的顺序。请查看URLConnection JavaDocs中的这些方法-setRequestProperty不应该在mUrl.openConnection()上建立连接后调用。它可能第一次工作是因为连接已经建立,然后您正在更改设置,这些设置在下一次尝试建立连接之前不会影响任何内容。尝试使用HttpURLConnection构造函数,这样您就可以在设置属性后调用connect()。


1
谢谢您的答复,但我担心那样行不通。这是 API 文档说的:只能在实例化之后但在连接到远程资源之前设置 URLConnection。在我的情况下,真正的连接发生在 conn.getInputStream(),而那是在设置标题之后。 - ggomeze

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接