从Android App与服务器通信时出现各种HTTP错误

 bunnyvivi 发布于 2022-12-31 12:30

更新时间:2015年1月4日

我还有这些问题.我们的应用程序的用户增加了,我看到了所有类型的网络错误.每当应用程序出现网络相关错误时,我们的应用程序就会发送电子邮件.

我们的应用程序进行了金融交易 - 因此重新提交并不是真正的幂等 - 因此非常害怕启用HttpClient的重试功能.我们在服务器上做了某种响应缓存来处理用户明确完成的重新提交.但是,仍然没有解决方案,没有糟糕的用户体验.

原始问题

我有一个Android应用程序,它发布数据作为用户操作的一部分.数据包含少量图像,我将它们打包为Protobuf消息(实际上是字节数组),并通过HTTPS连接将其发布到服务器.

尽管应用程序在大多数情况下都能正常工作,但我们偶尔会看到连接错误.由于我们在相对较慢的网络区域(2G连接)中有一些用户,因此问题变得更加明显.然而,问题不仅限于连接速度慢的区域,客户使用WiFi和3G连接也会出现问题.

以下是我们在App日志中注意到的一些例外情况

下面的一个发生在5分钟后,因为我已将Socket超时设置为5分钟.该应用程序试图在这种情况下发布145kb的数据

堆栈跟踪java.net.SocketTimeoutException:在org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_read(本机方法)中读取超时时间,位于org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl $ SSLInputStream.read( OpenSSLSocketImpl.java:662)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:写入错误:ssl = 0x5e4f4640:系统调用期间的I/O错误,org.apache中的org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_write(本机方法)中的管道损坏. harmony.xnet.provider.jsse.OpenSSLSocketImpl $ SSLOutputStream.write(OpenSSLSocketImpl.java:704)位于org.apache.http.impl的org.apache.http.impl.io.AbstractSessionOutputBuffer.write(AbstractSessionOutputBuffer.java:109). io.ContentLengthOutputStream.write(ContentLengthOutputStream.java:113)

1分钟后发生了一次.

堆栈跟踪javax.net.ssl.SSLException:org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake上org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_do_handshake(本地方法)的对等关闭连接(OpenSSLSocketImpl.java:378)org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl $ SSLInputStream.(OpenSSLSocketImpl.java:634)at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.getInputStream(OpenSSLSocketImpl. Java的:605)

77秒后发生了一次

堆栈跟踪javax.net.ssl.SSLException:SSL握手中止:ssl = 0x5e2baf00:系统调用期间的I/O错误,org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_do_handshake(本机方法)中的对等连接重置在org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:378)org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl $ SSLInputStream.(OpenSSLSocketImpl.java:634)at org. org.apache.http.impl.io.SocketInputBuffer上的apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.getInputStream(OpenSSLSocketImpl.java:605).(SocketInputBuffer.java:70)

15秒后发生一次以下(连接超时设置为15秒)

拍摄时间:15081堆栈跟踪org.apache.http.conn.ConnectTimeoutException:连接到org.apache.http.conn.scheme.PlainSocketFactory.connectSocket上的/103.xx.xx.xx:443超时(PlainSocketFactory.java:121 )org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:144)位于org.apache.http的org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164). impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119)at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:365)

以下是我用于发布请求的源代码片段

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手机

对此的任何意见都将受到高度赞赏.非常感谢!

更新:

    我注意到我们没有关闭Connection Manager.所以在我使用http客户端的代码的finally块中添加了下面的代码.

  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分钟作为小型请求的套接字超时,对于高达75 KB及更高的数据包分别使用3分钟和6分钟.

但是,这些错误已大大减少,而且我发现100个请求中有1个失败,而我的代码的早期版本则是10个请求中的1个.

1 个回答
  • 我最近不得不对我公司的应用程序进行详尽的分析,因为我们看到了一堆类似的错误而且不知道为什么.我们最终发布了自定义应用程序,它们将连接时间,错误,信号质量等记录到文件中.几周之后就这样做了.收集数以千计的数据点.请记住,我们在应用程序打开时保持持久连接.

    事实证明,我们的大多数错误来自交换网络.这对普通用户来说实际上很常见.因此,假设用户正在使用EDGE小区网络,然后在WIFI范围内行走,反之亦然.发生这种情况时,Android会逐字地切断单元连接,并与WIFI建立全新的连接.从应用程序的角度来看,它类似于打开飞行模式然后再次将其重新打开.这甚至在小区网络内切换时发生.例如,LTE到HSPA +.每次发生这种情况,Android都会关闭网络连接改变广播.

    在您列出的那些中,此行为导致以下类似错误:

    javax.net.ssl.SSLException:写入错误:ssl = 0x5e4f4640

    javax.net.ssl.SSLException:SSL握手中止:

    有时网络交换机很快,有时很慢.事实证明,我们没有使用快速开关及时清理我们的资源.因此,我们尝试使用陈旧/旧的TCP连接重新连接到我们的服务器,这些连接引发了更多奇怪的错误.

    所以我猜想,如果你长时间保持连接,那么预计会看到手机不断在网络之间切换,尤其是在信号较弱时.当发生网络切换时,您将看到SSLExeptions,这是完全正常的.只需要确保清理资源并正确重新连接.

    2022-12-31 12:33 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有