为什么android HttpURLConnection没有发回FIN?

Vis*_*ram 7 java android tcp keep-alive httpurlconnection

从我的Android应用程序中,我想将数据发布到服务器并获取响应,处理它然后发回并获得另一个请求.由于它是持续的沟通,直到没有更多的响应过程,我更喜欢HttpURLConnectionhttp.keepAlive = true.

我尝试重用套接字是成功的,但我面临的问题是:

  1. 我试图从客户端(Android应用程序)启动关闭,因为如果终止从服务器启动,则服务器进入 TIME_WAIT状态.我不希望我的服务器进入该状态,所以我更喜欢我的客户端启动终止.但不幸的是,我找不到合适的方法来做到这一点HttpURLConnection
  2. 经过几个小时的搜索,我放弃了上面的尝试,然后keepalivetimeout在服务器发送时启动Close ,但是当服务器发送时FIN,客户端只响应ACK,因为FIN_WAIT_2在服务器和CLOSE_WAIT代理中保持了连接 .

数据包日志片段

源代码:

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();
}
Run Code Online (Sandbox Code Playgroud)

有人可以帮忙吗?

m4k*_*tub 5

当您使用http.keepAlive = true连接时,开始在连接池中回收并保持打开状态.即使服务器关闭了它仍在监听的连接,客户端仍然认为它可以发送数据.毕竟,服务器FIN只说服务器不会发送更多数据.

由于连接池的内部逻辑无法访问,因此您几乎无法控制.然而,当你使用https,你有一个开放的窗口,较低层,并且可以访问所创建的Socket通过HttpsURLConnection.setSSLSocketFactory(SSLSocketFactory).

您可以为默认工厂(SSLSocketFactory.getDefault())创建一个允许关闭的包装器Socket.下面的简化:

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

    ...

}
Run Code Online (Sandbox Code Playgroud)

当您关闭底层套接字时,该连接不应该有资格进行回收,并将被丢弃.因此,如果您这样做closeLastSocket,disconnect并且连接甚至不应该转到池中,如果您执行相反的连接,则只有在创建新连接时才会丢弃该连接.


Ser*_*nko 0

我在那里发现了一些有趣的想法

以下是关于 FIN 的一些要点:

这里重要的是要理解调用 connection.disconnent()并不能保证触发 FIN。来自以下文档HttpURLConnection

读取响应正文后,HttpURLConnection应通过调用 来关闭disconnect()。断开连接会释放连接所持有的资源,以便可以关闭或重用它们。

这里的重点是可能:正如您从前面的屏幕截图中看到的,是服务器决定在某个时刻终止连接,而不是我们调用断开连接,因为第一个 FIN 是由目的地而不是源发送的。