2015-06-04 24 views
7

Von meiner Android App wollte ich Daten auf dem Server veröffentlichen und die Antwort zurückbekommen, sie verarbeiten, dann zurücksenden und eine weitere Anfrage erhalten. Da es sich um eine kontinuierliche Kommunikation bis keine Reaktion mehr auf den Prozess handelt, bevorzuge ich mit HttpURLConnectionhttp.keepAlive = true.Warum sendet Android HttpURLConnection nicht zurück FIN?

Und mein Versuch Buchse wieder zu verwenden ist erfolgreich, aber die Probleme, die ich sind bin vor:

  1. ich von Client (Android App) In der Nähe zu initiieren bin versucht, denn wenn die Beendigung von dann Server-Server gestartet geht zu TIME_WAIT Zustand. Und ich möchte nicht, dass mein Server in diesen Zustand wechselt, daher bevorzugte ich, dass mein Client die Kündigung einleitete. Aber leider Ich finde keine geeignete Art und Weise, es zu tun mit HttpURLConnection
  2. Nach Stunden des Suchens gab ich tun die oben Versuch und ging mit von Server Schließen Einleitung basierend auf keepalivetimeout, aber wenn Server sendet FIN, antwortet Kunde mit nur ACK, wegen dieser Verbindung auf FIN_WAIT_2 gehalten in Server und CLOSE_WAIT im Agenten.

Packets log snippet

Source Code:

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(); 
} 

Kann jemand helfen?

+0

Der 'geeignete Weg, es mit 'HttpURLConnection' zu tun ist' HttpURLConnection.disconnect(). 'Aber es ist nur ein Hinweis. – EJP

+0

@EJP Ich habe disconnect() am Ende der Transaktion verwendet, aber ich finde keinen Unterschied, können Sie bitte mehr ausarbeiten. –

Antwort

0

fand ich einige interessante Gedanken there

Hier einige Outtakes über FIN:

Hier ist es wichtig, dass Aufruf zu verstehen connection.disconnent() nicht FIN Auslösung garantiert. Von der Dokumentation von HttpURLConnection:

Sobald der Antworttext gelesen wurde, werden die HttpURLConnection sollte durch den Aufruf disconnect() geschlossen. Beim Trennen der Verbindung werden die von einer Verbindung gehaltenen Ressourcen freigegeben, sodass sie geschlossen oder wiederverwendet werden können.

Die Betonung liegt hier auf Mai: wie man aus dem vorherigen Screenshot sehen, es war der Server, den die Verbindung an einem gewissen Punkt zu beenden entschieden, und unser Ruf nicht zu trennen, da die ersten FIN ist gesendet vom Ziel nicht die Quelle.

5

Wenn Sie http.keepAlive = true verwenden, werden Verbindungen in einem Verbindungspool wiederverwendet und offen gehalten.Selbst wenn der Server die Verbindung schließt, hört er immer noch zu und der Client denkt kann Daten senden. Immerhin sagt der Server FIN nur sagt der Server wird nicht mehr Daten senden.

Da die interne Logik des Verbindungspools nicht zugänglich ist, haben Sie wenig Kontrolle. Wenn Sie jedoch https verwenden, haben Sie ein offenes Fenster zu den unteren Schichten und können auf die erstellten Socket bis HttpsURLConnection.setSSLSocketFactory(SSLSocketFactory) zugreifen.

Sie können einen Wrapper für die Standard-Factory erstellen (SSLSocketFactory.getDefault()), der das Schließen des Socket ermöglicht. So etwas wie die Vereinfachung Gebrüll:

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(); 
    } 

    ... 

} 

Wenn Sie die zugrunde liegende Buchse schließen Sie dann sollte die Verbindung nicht für das Recycling in Betracht und werden verworfen. Wenn Sie also closeLastSocket und disconnect tun, sollte die Verbindung nicht einmal zum Pool gehen und wenn Sie die andere Möglichkeit nutzen, sollte die Verbindung nur verworfen werden, wenn Sie eine neue Verbindung erstellen.

Verwandte Themen