volley studio example espaƱol android httpurlconnection

studio - post get android



Android HttpUrlConnection EOFException (7)

Me gustaría saber si hay problemas conocidos en Android con las solicitudes HttpUrlConnection y POST. Estamos experimentando EOFExceptions intermitentes al realizar solicitudes POST desde un cliente de Android. Volver a intentar la misma solicitud eventualmente funcionará. Aquí hay un rastro de pila de muestra:

java.io.EOFException at libcore.io.Streams.readAsciiLine(Streams.java:203) at libcore.net.http.HttpEngine.readResponseHeaders(HttpEngine.java:579) at libcore.net.http.HttpEngine.readResponse(HttpEngine.java:827) at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:283) at libcore.net.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:497) at libcore.net.http.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:134)

Hay muchos informes y publicaciones de errores similares al desbordamiento de la pila, pero no puedo entender si realmente hay un problema y, de ser así, qué versiones de Android se ven afectadas y cuál es la solución / solución alternativa propuesta.

Estos son algunos de los informes similares a los que me refiero:

Aquí hay una solución potencial para el marco de Android

Sé que había un problema con las conexiones envenenadas en el grupo de conexiones en pre-Froyo, pero estos problemas están ocurriendo exclusivamente en nuevos dispositivos ICS +. Si hubiera algún problema en dispositivos posteriores, esperaría algún tipo de documentación oficial de Android sobre el problema.


Esta solución alternativa tiende a ser confiable y eficiente:

static final int MAX_CONNECTIONS = 5; T send(..., int failures) throws IOException { HttpURLConnection connection = null; try { // initialize connection... if (failures > 0 && failures <= MAX_CONNECTIONS) { connection.setRequestProperty("Connection", "close"); } // return response (T) from connection... } catch (EOFException e) { if (failures <= MAX_CONNECTIONS) { disconnect(connection); connection = null; return send(..., failures + 1); } throw e; } finally { disconnect(connection); } } void disconnect(HttpURLConnection connection) { if (connection != null) { connection.disconnect(); } }

Esta implementación se basa en el hecho de que el número predeterminado de conexiones que se pueden abrir con un servidor es 5 (Froyo - KitKat). Esto significa que pueden existir hasta 5 conexiones obsoletas, cada una de las cuales tendrá que cerrarse.

Después de cada intento fallido, la propiedad Connection:close request hará que el motor HTTP subyacente cierre el socket cuando se llame a connection.disconnect() . Al volver a intentar hasta 6 veces (conexiones máximas + 1), nos aseguramos de que al último intento siempre se le otorgue un nuevo socket.

La solicitud puede experimentar latencia adicional si no hay conexiones EOFException , pero eso es ciertamente mejor que una EOFException . En ese caso, el intento de envío final no cerrará inmediatamente la conexión recién abierta. Esa es la única optimización práctica que se puede hacer.

En lugar de confiar en el valor mágico predeterminado de 5 , es posible que usted mismo pueda configurar las propiedades del sistema. Tenga en cuenta que a esta propiedad se accede mediante un bloque de inicializador estático en ConnectionPool.java de KitKat, y funciona así en versiones anteriores de Android. Como resultado, la propiedad se puede usar antes de que tenga la oportunidad de configurarla.

static final int MAX_CONNECTIONS = 5; static { System.setProperty("http.maxConnections", String.valueOf(MAX_CONNECTIONS)); }


Esto funcionó para mí.

public ResponseObject sendPOST(String urlPrefix, JSONObject payload) throws JSONException { String line; StringBuffer jsonString = new StringBuffer(); ResponseObject response = new ResponseObject(); try { URL url = new URL(POST_URL); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setDoInput(true); connection.setDoOutput(true); connection.setReadTimeout(10000); connection.setConnectTimeout(15000); connection.setRequestMethod("POST"); connection.setRequestProperty("Accept", "application/json"); connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); OutputStream os = connection.getOutputStream(); os.write(payload.toString().getBytes("UTF-8")); os.close(); BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream())); while ((line = br.readLine()) != null) { jsonString.append(line); } response.setResponseMessage(connection.getResponseMessage()); response.setResponseReturnCode(connection.getResponseCode()); br.close(); connection.disconnect(); } catch (Exception e) { Log.w("Exception ",e); return response; } String json = jsonString.toString(); response.setResponseJsonString(json); return response; }


La biblioteca HttpURLConnection mantiene internamente un grupo de conexiones. Por lo tanto, cada vez que se envía una solicitud, primero comprueba si ya hay una conexión existente en el grupo, en función de la cual decide crear una nueva.

Estas conexiones no son más que sockets, y esta biblioteca por defecto no cierra estos sockets. A veces puede suceder que una conexión (socket) que no se está utilizando actualmente y que está presente en el grupo ya no se pueda utilizar, ya que el Servidor puede optar por finalizar la conexión después de un tiempo. Ahora, dado que la conexión está cerrada por el servidor, la biblioteca no lo sabe y asume que la conexión / socket aún está conectado. Por lo tanto, envía la nueva solicitud utilizando esta conexión obsoleta y, por lo tanto, obtenemos EOFException.

La mejor manera de manejar esto es verificar los encabezados de respuesta después de cada solicitud que envíe. El servidor siempre envía una "Conexión: Cerrar" antes de finalizar una conexión (HTTP 1.1). Entonces, puedes usar getHeaderField () y verificar el campo "Conexión". Otra cosa a tener en cuenta es que el servidor SOLO envía este campo de conexión cuando está por terminar la conexión. Por lo tanto, debe codificar esto con la posibilidad de obtener un "nulo" en el caso normal (cuando el servidor no está cerrando la conexión)


Nuestra conclusión es que hay un problema en la plataforma de Android. Nuestra solución fue atrapar la EOFException y volver a intentar la solicitud N número de veces. A continuación está el pseudo código:

private static final int MAX_RETRIES = 3; private ResponseType fetchResult(RequestType request) { return fetchResult(request, 0); } private ResponseType fetchResult(RequestType request, int reentryCount) { try { // attempt to execute request } catch (EOFException e) { if (reentryCount < MAX_RETRIES) { fetchResult(request, reentryCount + 1); } } // continue processing response }


Sí. Hay un problema en la plataforma Android, específicamente, en Android libcore con la versión 4.1-4.3.

El problema se introduce en esta confirmación: https://android.googlesource.com/platform/libcore/+/b2b02ac6cd42a69463fd172531aa1f9b9bb887a8

Android 4.4 cambió http lib a "okhttp", que no tiene este problema.

Problema explicado de la siguiente manera:

En Android 4.1-4.3, cuando está utilizando URLConnection / HttpURLConnection para POST con "ChunkedStreamingMode" o "FixedLengthStreamingMode" establecido , URLConnection / HttpURLConnection no volverá a intentar silenciosamente si la conexión reutilizada está desactualizada. Debe volver a intentar POST a lo sumo "http.maxConnections + 1" veces en su código, tal como lo sugieren las respuestas anteriores.


Sospecho que podría ser el servidor el culpable aquí, y el HttpURLConnection no es tan indulgente como otras implementaciones. Esa fue la causa de mi EOFException. Sospecho que en mi caso esto no sería intermitente (lo solucioné antes de probar la solución N reintentar), por lo que las respuestas anteriores se relacionan con otros problemas y serán una solución correcta en esos casos.

Mi servidor estaba usando Python SimpleHTTPServer y estaba asumiendo erróneamente todo lo que tenía que hacer para indicar que el éxito fue el siguiente:

self.send_response(200)

Esto envía la línea de encabezado de respuesta inicial, un servidor y un encabezado de fecha, pero deja la transmisión en el estado en el que también puede enviar encabezados adicionales. HTTP requiere una nueva línea adicional después de los encabezados para indicar que están terminados. Parece que si esta nueva línea no está presente cuando intentas obtener el cuerpo del resultado InputStream o el código de respuesta, etc., con HttpURLConnection, arroja la EOFException (lo cual es realmente razonable, pensando en ello). Algunos clientes HTTP aceptaron la respuesta breve e informaron el código de resultado exitoso que me condujo señalando con el dedo injustamente a HttpURLConnection.

Cambié mi servidor para hacer esto:

self.send_response(200) self.send_header("Content-Length", "0") self.end_headers()

No más EOFException con ese código.


connection.addRequestProperty ("Aceptar-Codificar", "gzip");

es la respuesta