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.
- Certificado SSL para los servicios web REST (utilizados por Android)?
- Android WebView setCertificate emite problemas de SSL
- Error de Android en webview.loadUrl () - Ancla de confianza para la ruta de certificación no encontrada
- Generación e instalación de certificados SSL
- Javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Ancla de confianza para la ruta de certificación no encontrada
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:
- 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(); }
- 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;
- 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:
-
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. -
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.
- Tablet no puede descargar el archivo .apk en algunos navegadores
- Certificado ssl de android
- Cordova App en Android usando SSL enviando multipart / form-data causes Error de la aplicación de rack: # <EOFError: cuerpo de contenido incorrecto>
- Android L - Sin certificado de igualdad
- Error "No se pudo cargar la biblioteca SSL" en Android con TidHTTP
- Comportamiento de tipo navegador en certificados no válidos / autofirmados
- ¿Cómo habilito TLSv1.1 + en Xamarin Android (API 16 - 19)?
- Javax.net.ssl.SSLHandshakeException: Handshake falló en Android 5.0.0 cuando SSLv2 y SSlv3 están deshabilitados (sólo TLS) (y mayores)
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.