Variedad de errores HTTPs al comunicarse con el servidor desde la aplicación Android

ACTUALIZACIÓN: 04 Ene 2015

Todavía tengo estos problemas. Los usuarios de nuestra aplicación han aumentado y veo todo tipo de errores de red. Nuestra aplicación envía correos electrónicos cada vez que hay un error relacionado con la red en la aplicación.

Nuestra aplicación realiza transacciones financieras, por lo que los reenvíos no son realmente idempotentes, por lo que estamos muy asustados de permitir la función de reintento de HttpClient. Hemos hecho algún tipo de caché de respuesta en el servidor para manejar los reenvíos hechos explícitamente por el usuario. Sin embargo, todavía no hay solución que funcione sin mala experiencia del usuario.

Pregunta original

Tengo una aplicación para Android que publica datos como parte de la operación del usuario. Los datos incluyen pocas imágenes y los embalo como mensaje de Protobuf (matriz de bytes, en efecto) y lo posteo al servidor a través de la conexión HTTPS.

Aunque la aplicación funciona bien para la mayor parte, pero estamos viendo errores de conexión de vez en cuando. El problema se ha vuelto más pronunciado ahora que tenemos algunos usuarios en áreas de red relativamente lentas (conexiones 2G). Sin embargo, el problema no se limita a las áreas de conexiones lentas, el problema se ve con los clientes que utilizan conexiones WiFi y 3G.

Estas son algunas excepciones que observamos en los registros de nuestra aplicación

Debajo de uno sucede después de 5 minutos, como yo había fijado el tiempo de espera del zócalo a 5 minutos. La aplicación estaba tratando de publicar 145kb de datos en este caso

Ruta de la pila java.net.SocketTimeoutException: Se ha agotado el tiempo de espera en org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_read (Método nativo) en org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl $ SSLInputStream.read OpenSSLSocketImpl.java:662) en org.apache.http.impl.io.AbstractSessionInputBuffer.fillBuffer (AbstractSessionInputBuffer.java:103) en org.apache.http.impl.io.AbstractSessionInputBuffer.readLine (AbstractSessionInputBuffer.java:191)

Debajo de uno sucedió 2.5 minutos (el tiempo de espera del zócalo fue fijado a 5 minutos), cliente estaba enviando 144kb de datos

Javax.net.ssl.SSLException: Error de escritura: ssl = 0x5e4f4640: Error de E / S durante la llamada al sistema, Broken pipe en org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_write (Método nativo) en org.apache. Harmony.xnet.provider.jsse.OpenSSLSocketImpl $ SSLOutputStream.write (OpenSSLSocketImpl.java:704) en org.apache.http.impl.io.AbstractSessionOutputBuffer.write (AbstractSessionOutputBuffer.java:109) en org.apache.http.impl. Io.ContentLengthOutputStream.write (ContentLengthOutputStream.java:113)

Por debajo de uno ocurrió después de 1 minuto.

Rastreo de la pila javax.net.ssl.SSLException: Conexión cerrada por peer en org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_do_handshake (Método nativo) en org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake (OpenSSLSocketImpl.java:378) en org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl $ SSLInputStream. (OpenSSLSocketImpl.java:634) en org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.getInputStream (OpenSSLSocketImpl. Java: 605)

Por debajo de uno pasó después de 77 segundos

Rastreo de la pila javax.net.ssl.SSLException: Protocolo SSL abortado: ssl = 0x5e2baf00: Error de E / S durante la llamada al sistema, Conexión restablecida por peer en org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_do_handshake (Método nativo) En org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake (OpenSSLSocketImpl.java:378) en org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl $ SSLInputStream. (OpenSSLSocketImpl.java:634) en org. Apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.getInputStream (OpenSSLSocketImpl.java:605) en org.apache.http.impl.io.SocketInputBuffer. (SocketInputBuffer.java:70)

Por debajo de uno ocurrió después de 15 segundos (tiempo de espera de conexión se establece en 15 segundos)

Tiempo tomado: 15081 Ruta de la pila org.apache.http.conn.ConnectTimeoutException: Conectarse a /103.xx.xx.xx:443 agotado el tiempo en org.apache.http.conn.scheme.PlainSocketFactory.connectSocket (PlainSocketFactory.java:121 ) En org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection (DefaultClientConnectionOperator.java:144) en org.apache.http.impl.conn.AbstractPoolEntry.open (AbstractPoolEntry.java:164) en org.apache.http. Impl.conn.AbstractPooledConnAdapter.open (AbstractPooledConnAdapter.java:119) en org.apache.http.impl.client.DefaultRequestDirector.execute (DefaultRequestDirector.java:365)

He aquí los fragmentos de código fuente que utilizo para publicar el 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(); } } 

He leído algunos foros indicando que debemos usar la clase HttpUrlConnection. Realicé cambios de código para usar https://code.google.com/p/basic-http-client/ como una corrección. Aunque trabajó en mi teléfono Samsung, parecía tener algún problema en el cliente de teléfono estaba utilizando, ni siquiera fue capaz de conectarse a nuestro sitio. Tuve que volver a rodar, aunque puedo volver a mirarlo si la causa raíz se puede fijar a DefaultHttpClient.

Nuestro servidor web es nginx y nuestro servicio web funciona con Apache Tomcat. Los clientes usan principalmente teléfonos Android 4.1 o superiores. El cliente de cuyo teléfono he recuperado por encima de los rastros de la pila está usando el teléfono Micromax A110Q con Android 4.2.1

Cualquier información sobre esto será muy apreciada. ¡Muchas gracias!

Actualizar:

  1. Me había dado cuenta de que no estábamos cerrando el Connection Manager. Por lo tanto, agregó debajo de código en finalmente el bloque del código donde uso el cliente http.
  if (client != null) { client.getConnectionManager().shutdown(); } 
  1. La configuración nginx actualizada para aceptar datos hasta el tamaño de 5M como su valor predeterminado es 1Mb y algunos clientes estaban enviando más de 1 MB y el servidor estaba cortando la conexión con el error 413.
 client_max_body_size 5M; 
  1. También aumentó el tiempo de espera de lectura del proxy nginx de modo que espera más tiempo para obtener datos del cliente.
 proxy_read_timeout 300; 

Con los cambios anteriores, los errores se han reducido un poco. En la última semana, veo dos tipos de errores:

  1. org.apache.http.conn.ConnectTimeoutException: Connect to /103.xx.xx.xxx:443 timed out – Esto ocurre en 15 segundos, que es mi tiempo de espera de conexión. Estoy asumiendo que esto sucede como cliente no puede alcanzar al servidor debido a la lentitud de la red o como @JaySoyer señaló, puede ser debido a la conmutación de red.

  2. java.net.SocketTimeoutException: SSL handshake timed out at org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_do_handshake(Native Method) . Esto sucede al expirar el tiempo de espera del socket. Ahora estoy usando 1 minuto como tiempo de espera de socket para peticiones pequeñas, y 3 y 6 minutos para paquetes de hasta 75 KB y más alto, respectivamente.

Sin embargo, estos errores se han reducido considerablemente, y estoy viendo un fracaso en 100 peticiones, en comparación con la versión anterior de mi código, donde fue 1 de cada 10 solicitudes.

Hace poco tuve que hacer un análisis exhaustivo de la aplicación de mi empresa, ya que estábamos viendo un montón de errores similares y no sabía por qué. Terminamos entregando aplicaciones personalizadas que literalmente registraban sus tiempos de conexión, errores, calidad de señal, etc. a un archivo. Lo hicimos durante semanas. Recoge miles de puntos de datos. Tenga en cuenta que mantenemos una conexión persistente mientras la aplicación está abierta.

Resulta que la mayoría de nuestros errores eran de redes de conmutación. Esto es realmente muy común para un usuario promedio. Así que digamos que un usuario está usando una red de celdas EDGE, luego camina dentro del rango WIFI o viceversa. Cuando esto ocurre, Android literalmente corta la conexión de la célula y hace una conexión completamente nueva con el WIFI. Desde la perspectiva de las aplicaciones, es similar a activar el modo avión y luego retroceder de nuevo. Esto ocurre incluso cuando se cambia dentro de las redes de una celda. Por ejemplo, LTE a HSPA +. Cada vez que esto sucede, Android disparará la conectividad de red cambiado de difusión.

De los que enumeró, este comportamiento provocaba los siguientes errores similares:

  • Javax.net.ssl.SSLException: Error de escritura: ssl = 0x5e4f4640
  • Javax.net.ssl.SSLException: Protocolo SSL abortado:

A veces el interruptor de red era rápido, a veces lento. Resulta que no estábamos limpiando nuestros recursos a tiempo con los conmutadores rápidos. Como resultado, estábamos intentando volver a conectarnos a nuestros servidores con conexiones TCP obsoletas / antiguas que arrojaban aún más errores extraños.

Así que supongo que el llevar es, si usted está manteniendo una conexión durante un largo período de tiempo, esperar a ver el teléfono constantemente cambiar entre las redes, especialmente cuando la señal es débil. Cuando se produce este cambio de red, verá SSLExeptions y es completamente normal. Solo tienes que asegurarte de limpiar tus recursos y reconectarte correctamente.

Dado que se trata de lo que parece una mala conectividad de red, considere un cliente HTTP más tolerante a fallos. El que me gusta es OkHTTP . De su descripción:

OkHttp persevera cuando la red es problemática: se recuperará silenciosamente de problemas comunes de conexión. Si su servicio tiene varias direcciones IP, OkHttp intentará direcciones alternativas si falla la primera conexión. Esto es necesario para IPv4 + IPv6 y para servicios alojados en centros de datos redundantes. OkHttp inicia nuevas conexiones con características TLS modernas (SNI, ALPN), y vuelve a SSLv3 si el apretón de manos falla.

La implementación sería en su mayor parte un reemplazo.

  • El ancla de la confianza para la trayectoria del certificado no encontrada al usar sobre 3g pero trabaja muy bien sobre WiFi
  • Uso de un certificado autofirmado para crear una conexión cliente-servidor segura en android
  • Necesitan ayuda para entender las cadenas de certificados
  • Cómo deshabilitar SSLbackbackback con okhttp
  • Android - Https conexión a un servicio WCF de .NET da SSLException: "Ningún certificado de compañero"
  • Android 4.4.2 Protocolo SSL abortado
  • Alerta de seguridad de Google Play para TrustManager inseguro
  • HTTPS con Retrofit y OkHttp Handshake falló
  • Android ssl: javax.net.ssl.SSLPeerUnverifiedException: Ningún certificado de igual (una vez más)
  • HttpsUrlConnection: Ancla de confianza para ruta de certificación no encontrada en 2.3
  • Consumir servicio WCF en SSL en Android
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.