¿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:
- Implementación cliente / servidor de socket TCP de Android
- La mejor estrategia de comunicación entre el cliente de Android y el servidor de .Net C #
- Comunicación TCP de Android con C # Server -> socket.Close
- Android VpnService, reenvío de paquetes
- Tcp conexión otro dispositivo android, basado en # teléfono en lugar de IP?
- 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 conHttpURLConnection
- 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íaFIN
, el cliente responde con sóloACK
, debido a que la conexión se mantuvo enFIN_WAIT_2
en servidor yCLOSE_WAIT
en agente.
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?
- Envío de datos TCP desde Android (como cliente) - ¿no se envían datos?
- ConnectionTimeout versus SocketTimeout
- android Apache httpclient archivo de carga ,. tiene algún error de reensamble TCP extraño
- ¿Cómo los sockets TCP / IP de Java informan sobre el éxito o el fallo de la transmisión en la aplicación?
- Descodificación de H264 streaming usando android de bajo nivel api
- Mensajería instantánea sobre xmpp o websocket
- El servidor de inicio de adb.exe falló en el estudio de android
- El consumo de la batería mientras TCP está abierto para ADB vía wifi puede conectar
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 adisconnect()
. 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.