从安卓应用程序与服务器通信时出现各种HTTP错误
最后更新: 04 01 2015
我仍然有这些问题。我们应用程序的用户增加了,我看到了各种网络错误。我们的应用程序每次应用程序上出现与网络相关的错误时都会发送电子邮件。
我们的应用程序执行金融交易 - 因此重新提交并不是真正幂等的 - 所以非常害怕启用HttpClient的重试功能。我们已经在服务器上进行了某种响应缓存,以处理用户显式完成的重新提交。但是,仍然没有解决方案可以在没有不良用户体验的情况下工作。
原始问题
我有一个Android应用程序,它将数据作为用户操作的一部分发布。数据包括一些图像,我将它们打包为Protobuf消息(实际上是字节数组),并通过HTTPS连接将其发布到服务器。
尽管该应用程序在大多数情况下运行良好,但我们偶尔会看到连接错误。现在,由于我们在相对较慢的网络区域(2G连接)中有一些用户,因此问题变得更加明显。但是,问题不仅限于连接速度慢的区域,使用WiFi和3G连接的客户可以看到问题。
以下是我们在应用程序日志中注意到的一些例外情况
以下情况发生在5分钟后,因为我已将Socket超时设置为5分钟。在这种情况下,该应用程序试图发布145kb的数据
Stack trace java.net.SocketTimeoutException: Read timed out at org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_read(Native Method) at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl$SSLInputStream.read(OpenSSLSocketImpl.java:662) at org.apache.http.impl.io.AbstractSessionInputBuffer.fillBuffer(AbstractSessionInputBuffer.java:103) at org.apache.http.impl.io.AbstractSessionInputBuffer.readLine(AbstractSessionInputBuffer.java:191)
低于一个发生2.5分钟(套接字超时设置为5分钟),客户端正在发送144kb的数据
javax.net.ssl.SSLException: Write error: ssl=0x5e4f4640: I/O error during system call, Broken pipe at org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_write(Native Method) at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl$SSLOutputStream.write(OpenSSLSocketImpl.java:704) at org.apache.http.impl.io.AbstractSessionOutputBuffer.write(AbstractSessionOutputBuffer.java:109) at org.apache.http.impl.io.ContentLengthOutputStream.write(ContentLengthOutputStream.java:113)
1分钟后发生了以下一个。
Stack trace javax.net.ssl.SSLException: Connection closed by peer at org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_do_handshake(Native Method) at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:378) at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl$SSLInputStream.(OpenSSLSocketImpl.java:634) at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.getInputStream(OpenSSLSocketImpl.java:605)
以下一个发生在77秒后
Stack trace javax.net.ssl.SSLException: SSL handshake aborted: ssl=0x5e2baf00: I/O error during system call, Connection reset by peer at org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_do_handshake(Native Method) at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:378) at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl$SSLInputStream.(OpenSSLSocketImpl.java:634) at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.getInputStream(OpenSSLSocketImpl.java:605) at org.apache.http.impl.io.SocketInputBuffer.(套接字输入缓冲器.java:70)
15 秒后发生以下 1 次(连接超时设置为 15 秒)
时间 : 15081 Stack trace org.apache.http.conn.ConnectTimeoutException: Connect to /103.xx.xx.xx:443 time out out at org.apache.http.conn.scheme.PlainSocketFactory.connectSocket(PlainSocketFactory.java:121) at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:144) at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164) at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119) at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:365)
以下是我用于发布reqeust的源代码片段
HttpParams params = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(params, 15000); //15 seconds
HttpConnectionParams.setSoTimeout(params, 300000); // 5 minutes
HttpClient client = getHttpClient(params);
HttpPost post = new HttpPost(uri);
post.setEntity(new ByteArrayEntity(requestByteArray));
HttpResponse httpResponse = client.execute(post);
....
public static HttpClient getHttpClient(HttpParams params) {
try {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
SSLSocketFactory sf = new TrustAllCertsSSLSocketFactory(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", sf, 443));
ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
DefaultHttpClient client = new DefaultHttpClient(ccm, params);
// below line of code will disable the retrying of HTTP request when connection is timed
// out.
client.setHttpRequestRetryHandler(new DefaultHttpRequestRetryHandler(0, false));
return client;
} catch (Exception e) {
return new DefaultHttpClient();
}
}
我读过一些论坛,表明我们应该使用HttpUrlConnection类。我确实进行了代码更改,以使用 https://code.google.com/p/basic-http-client/ 作为热修复程序。虽然它适用于我的三星手机,但它似乎在手机客户使用的手机中存在一些问题,它甚至无法连接到我们的网站。我不得不回滚它,尽管如果根本原因可以固定到DefaultHttpClient,我可以重新考虑它。
OUr Web服务器是nginx,我们的Web服务在Apache Tomcat上运行。客户主要使用Android 4.1 +手机。我从其手机中检索到上述堆栈跟踪的客户正在使用带有Android 4.2.1的Micromax A110Q手机
对此的任何投入将不胜感激。多谢!
更新:
- 我注意到我们没有关闭连接管理器。所以在代码块中添加了下面的代码,我使用http客户端。
if (client != null) { client.getConnectionManager().shutdown(); }
- 更新了nginx配置以接受最大为5M的数据,因为其默认值为1Mb,并且某些客户端提交的数据超过1MB,并且服务器正在切断连接并出现413错误。
client_max_body_size 5M;
- 还增加了nginx代理读取超时,以便它等待更长的时间从客户端获取数据。
proxy_read_timeout 300;
通过上述更改,错误有所减少。在过去的一周里,我看到以下两种类型的错误:
org.apache.http.conn.ConnectTimeoutException: Connect to /103.xx.xx.xxx:443 timed out
- 这发生在15秒内,这是我的连接超时。我假设发生这种情况是因为客户端由于网络速度慢而无法访问服务器,或者正如@JaySoyer指出的那样,可能是由于网络切换。java.net.SocketTimeoutException: SSL handshake timed out at org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_do_handshake(Native Method)
.这发生在套接字超时到期时。我现在使用1分钟作为小请求的套接字超时,分别使用3分钟和6分钟作为最大75 KB及以上的数据包。
但是,这些错误已大大减少,我看到100个请求中有1个失败,而早期版本的代码是10个请求中的1个。