2015-06-04 10 views
7

Z mojej aplikacji na Androida chciałem opublikować dane na serwerze i odebrać odpowiedź, przetworzyć ją, a następnie odesłać i otrzymać kolejną prośbę. Ponieważ jest to ciągła komunikacja, dopóki nie ma więcej odpowiedzi na proces, wolę podążać z HttpURLConnection z http.keepAlive = true.Dlaczego funkcja HttpURLConnection Androida nie odsyła FIN?

A moja próba ponownego użycia Gniazdo jest sukcesem, ale problemy mam stojące są:

  1. próbuję zainicjować Close z Klientem (Android App), ponieważ jeśli wypowiedzenie zaczyna się od Następnie serwer Server przechodzi do stanu TIME_WAIT stan. I nie chcę, aby mój serwer działał w tym stanie, więc wolałam, aby mój Klient zainicjował wypowiedzenie. Ale niestety nie znajduję odpowiedni sposób to zrobić z HttpURLConnection
  2. Po godzinach poszukiwania dałem się na robienie powyższą próbę i poszedł z inicjowanie Close z serwera na podstawie keepalivetimeout, ale kiedy serwer wysyła FIN, klient odpowiada tylko ACK, z powodu tego połączenia odbywa się na FIN_WAIT_2 w serwerze i CLOSE_WAIT w agencie.

Packets log snippet

Kod źródłowy:

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

Czy ktoś może pomóc?

+0

"Odpowiednim sposobem, aby to zrobić z' HttpURLConnection 'jest 'HttpURLConnection.disconnect().' Ale to tylko podpowiedź. – EJP

+0

@EJP Użyłem disconnect() na końcu transakcji, ale nie znajduję żadnej różnicy, czy mógłbyś rozwinąć więcej. –

Odpowiedz

0

znalazłem kilka ciekawych myślom there

Oto kilka fragmentów filmów o FIN:

Tutaj ważne jest, aby zrozumieć, że nazywając connection.disconnent() nie gwarantuje wyzwalanie FIN. Z dokumentacji z HttpURLConnection:

Gdy ciało odpowiedź została odczytaniu HttpURLConnection powinien być zamknięty przez wywołanie disconnect(). Odłączenie zwalnia zasoby przechowywane przez połączenie, aby można je było zamknąć lub użyć ponownie.

Chodzi o to maja: jak widać z poprzedniego screenie, to serwer, który postanowił zakończyć połączenie w pewnym momencie, a nie nasze wezwanie, aby odłączyć, od pierwszego FIN jest wysłany przez miejsce docelowe, a nie źródło.

5

Podczas korzystania z połączeń http.keepAlive = true rozpoczyna się recykling, w puli połączeń i pozostaje otwarty.Nawet jeśli serwer zamknie połączenie, nadal będzie nasłuchiwał, a klient nadal będzie mógł wysyłać dane. W końcu serwer FIN mówi tylko, że serwer nie prześle więcej danych.

Ponieważ wewnętrzna logika puli połączeń jest niedostępna, masz niewielką kontrolę. Niemniej jednak, podczas korzystania z https, masz otwarte okno do niższych warstw i możesz uzyskać dostęp do utworzonych Socket do HttpsURLConnection.setSSLSocketFactory(SSLSocketFactory).

Można utworzyć opakowanie dla domyślnej fabryki (SSLSocketFactory.getDefault()), która pozwala zamknąć Socket. Coś jak uproszczenie poniżej:

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

    ... 

} 

Po zamknięciu leżącego poniżej gniazda połączenie nie powinno kwalifikować się do recyklingu i zostanie odrzucone. Jeśli więc robisz closeLastSocket i disconnect, połączenie nie powinno nawet iść do puli, a jeśli zrobisz odwrotnie, połączenie powinno zostać odrzucone tylko podczas tworzenia nowego połączenia.

Powiązane problemy