¿Por qué HttpURLConnection de Android no devuelve FIN?

Desde mi aplicación de Android quería enviar datos al servidor y obtener la respuesta de nuevo, el proceso a continuación, enviar de nuevo y obtener otra solicitud. Como es una comunicación continua hasta que no hay más respuesta al proceso, prefiero ir con HttpURLConnection con http.keepAlive = true .

Y mi intento de reutilizar socket es exitoso, pero los problemas que estoy enfrentando son:

  1. Estoy tratando de iniciar Close from Client (Android App), ya que si la terminación se inicia desde el servidor, el servidor pasa al estado TIME_WAIT . Y no quiero que mi servidor vaya a ese estado, así que preferí que mi cliente iniciara la terminación. Pero desafortunadamente no encuentro ninguna manera conveniente de hacerlo con HttpURLConnection
  2. Después de horas de búsqueda, dejé de hacer el intento anterior y fui con iniciar Cerrar desde el servidor basado en keepalivetimeout , pero cuando el servidor envía FIN , el cliente responde con sólo ACK , debido a que la conexión se mantuvo en FIN_WAIT_2 en servidor y CLOSE_WAIT en agente.

Snippet de registro de paquetes

Código fuente:

 private HttpStatus communicateWithMDMServer(String httpUrl, String dataToSend, boolean keepAlive) { HttpStatus status = new HttpStatus(HTTP_STATUS_FAILURE); try { initializeConnection(httpUrl,keepAlive); postDataToConnection(connection, dataToSend); status = readDataFromConnection(connection); } catch (MalformedURLException e) { MDMLogger.error("Failed to send data to server as the URL provided is not valid "+ e.getMessage()+"\n"); e.printStackTrace(); } catch (IOException e) { MDMLogger.error("Failed to send the status to the server : IOException "+ e.getMessage()+"\n"+e.getStackTrace()); e.printStackTrace(); readErrorStreamAndPrint(connection); } connection.disconnect(); return status; } /** * API to close connection, calling this will not force the connection to shutdown * this will work based on the Connection header set. * @param connection */ public void closeConnection(){ if(connection != null){ connection.disconnect(); } } /** * Used to initialize the HttpURLConnection for the given url * All properties required for connection are preset here * Connection Time Out : 20 Seconds * Connection Type : keep alive * Content Type : application/json;charset=UTF-8 * And also All certificates will be evaluated as Valid.[ TODO will be removed soon] * @param httpUrl * @return * @throws MalformedURLException * @throws IOException */ private void initializeConnection(String httpUrl, boolean keepAlive) throws MalformedURLException, IOException{ URL url = new URL(httpUrl); connection = (HttpsURLConnection) url.openConnection(); connection.setConnectTimeout(20000); connection.setReadTimeout(20000); connection.setDoInput(true); connection.setDoOutput(true); connection.setRequestMethod("POST"); //NO I18N connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8"); //NO I18N connection.setRequestProperty("Connection", "Keep-Alive"); //NO I18N } /** * API to post data to given connection * call to this API will close the @OutputStream * @param connection * @param data * @throws IOException */ private void postDataToConnection(URLConnection connection , String data) throws IOException{ OutputStream outStream = connection.getOutputStream(); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outStream)); writer.write(data); writer.flush(); writer.close(); outStream.close(); } /** * API to read error stream and log * @param connection */ private void readErrorStreamAndPrint(URLConnection connection){ try{ InputStream inStream = ((HttpURLConnection) connection).getErrorStream(); String responseData = ""; String line; BufferedReader br=new BufferedReader(new InputStreamReader(inStream)); while ((line=br.readLine()) != null) { responseData+=line; } MDMLogger.error("ErrorStream Says "+responseData); } catch (IOException ioe) { MDMLogger.debug("Exception on reading ErrorStream"); } } /** * API to read data from given connection and return {@code com.manageengine.mdm.framework.communication.HttpStatus} * call to this API will close the @InputStream * @param connection * @return * @throws IOException */ private HttpStatus readDataFromConnection(URLConnection connection) throws IOException{ HttpStatus status = new HttpStatus(HTTP_STATUS_FAILURE); int responseCode=((HttpURLConnection) connection).getResponseCode(); MDMLogger.info("Response Code: "+responseCode); InputStream inStream = connection.getInputStream(); MDMLogger.info("Response Header: "+connection.getHeaderFields()); String responseData = ""; if (responseCode == HttpURLConnection.HTTP_OK) { responseData = readStreamAsString(inStream); status.setStatus(HTTP_STATUS_SUCCESS); status.setUrlDataBuffer(responseData); MDMLogger.info("communicateWithMDMServer : Response is \n" + status.getUrlDataBuffer()); MDMLogger.info("Successfully send the data to server and received success response "); } else { status.setStatus(HTTP_STATUS_FAILURE); MDMLogger.error("Data sent successfully but failed to get the response and the response code is : " + responseCode); } inStream.close(); return status; } /** * Read the InputStream to String until EOF * Call to this API will not close @InputStream * @param inStream * @return * @throws IOException */ private String readStreamAsString(InputStream inStream) throws IOException{ StringBuilder responseData = new StringBuilder(); String line; BufferedReader br=new BufferedReader(new InputStreamReader(inStream)); while ((line=br.readLine()) != null) { responseData.append(line); } return responseData.toString(); } 

¿Alguien puede ayudar?

Cuando utiliza http.keepAlive = true conexiones http.keepAlive = true comienzan a ser recicladas, en una agrupación de conexiones, y se mantienen abiertas. Incluso si el servidor cierra la conexión sigue escuchando y el cliente sigue pensando que puede enviar datos. Después de todo, FIN del servidor sólo dice que el servidor no enviará más datos.

Dado que la lógica interna del conjunto de conexiones es inaccesible, tiene poco control. Sin embargo, al usar https , tiene una ventana abierta a las capas inferiores y puede acceder al Socket creado mediante HttpsURLConnection.setSSLSocketFactory(SSLSocketFactory) .

Puede crear un contenedor para la fábrica predeterminada ( SSLSocketFactory.getDefault() ) que permite cerrar el Socket . Algo como la simplificación abajo:

 public class MySSLSocketFactory extends SSLSocketFactory { private SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); private Socket last; public void closeLastSocket() { if (last != null) { last.close(); } } public Socket createSocket() throws IOException { return this.last = factory.createSocket(); } ... } 

Al cerrar el socket subyacente, la conexión no debe ser elegible para el reciclaje y se descartará. Así que si haces closeLastSocket y disconnect la conexión no debería ir a la piscina y si lo haces al revés, la conexión debe ser descartada sólo cuando se crea una nueva conexión.

Encontré algunos pensamientos interesantes allí

Aquí hay algunas tomas sobre FIN:

Aquí es importante entender que llamar a connection.disconnent() no garantiza la activación de FIN. De la documentación de HttpURLConnection :

Una vez que se ha leído el cuerpo de respuesta, la HttpURLConnection debe cerrar llamando a disconnect() . La desconexión libera los recursos retenidos por una conexión para que puedan ser cerrados o reutilizados.

El énfasis aquí está en mayo: como se puede ver en la captura de pantalla anterior, fue el servidor que decidió terminar la conexión en algún momento, y no nuestra llamada a desconectar, ya que el primer FIN es enviado por el destino no la fuente.

  • La mejor manera de evitar los efectos del algoritmo Nagle usando webSockets?
  • Largo tiempo de conexión TCP persistente en el Android
  • Obtener la puerta de enlace predeterminada mediante programación utilizando getHostAddress () de la clase InetAddress
  • ¿Podemos enviar datos desde un dispositivo Android a otro dispositivo android directamente (p2p) sin servidor en el medio?
  • Escribir en flujo de salida VpnService no proporciona ninguna respuesta
  • ¿Problemas con el uso de TCP y UDP en la misma aplicación?
  • Conexión adb sobre tcp no funciona ahora
  • La pila TCP XBox 360 no responde a sondas TCP Zero Window con una carga útil de 0 bytes
  • ¿Cómo mantener una conexión TCP establecida indefinidamente?
  • Socket IPv6 en Android
  • Exponiendo puertos en Android con React-Native
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.