【问题标题】:Why is android HttpURLConnection not sending back FIN?为什么android HttpURLConnection 不发回FIN?
【发布时间】:2015-08-18 20:20:19
【问题描述】:

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

而且我重用socket的尝试是成功的,但是我面临的问题是:

  1. 我正在尝试从客户端(Android 应用)启动关闭,因为如果 终止从服务器开始,然后服务器转到 TIME_WAIT 状态。而且我不希望我的服务器进入那种状态,所以我更喜欢我的客户端启动终止。但不幸的是 我找不到合适的方法来使用 HttpURLConnection
  2. 经过数小时的搜索后,我放弃了上述尝试并 开始从服务器关闭基于 keepalivetimeout,但是当服务器发送 FIN 时,客户端只回复 ACK,因为连接被搁置 FIN_WAIT_2 在服务器中,CLOSE_WAIT 在代理中。

源代码:

private HttpStatus communicateWithServer(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) {
        e.printStackTrace();
    } catch (IOException e) {
        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;
        }
        
    } catch (IOException ioe) {
        
    }
}


/**
 * API to read data from given connection and return 
 * 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();
    InputStream inStream = connection.getInputStream();
    String responseData = "";
    if (responseCode == HttpURLConnection.HTTP_OK) {
        responseData = readStreamAsString(inStream);
        status.setStatus(HTTP_STATUS_SUCCESS);
        status.setUrlDataBuffer(responseData);
    }
    else {
        status.setStatus(HTTP_STATUS_FAILURE);
    }
    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();
}

谁能帮忙?

【问题讨论】:

  • “使用HttpURLConnection' is HttpURLConnection.disconnect() 的合适方法。”但这只是一个提示。
  • @EJP 我在交易结束时使用了 disconnect() 但我没有发现任何区别,请您详细说明一下。

标签: java android tcp httpurlconnection keep-alive


【解决方案1】:

我发现了一些有趣的想法there

以下是关于 FIN 的一些总结:

在这里理解调用很重要 connection.disconnent() 不保证触发 FIN。来自 HttpURLConnection的文档

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

这里的重点是五月:从前面可以看出 屏幕截图,是服务器决定终止连接 在某些时候,而不是我们断开连接的调用,因为第一个 FIN 是 由目的地而不是源发送。

【讨论】:

    【解决方案2】:

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

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

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

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

    当你关闭底层套接字时,连接不应该被回收并且将被丢弃。因此,如果您执行closeLastSocketdisconnect,则连接甚至不应进入池,如果您以其他方式执行此操作,则只有在您创建新连接时才应丢弃该连接。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-01-21
      • 1970-01-01
      • 2012-07-04
      • 2019-03-11
      • 2018-10-09
      • 2011-07-02
      • 2012-12-07
      • 1970-01-01
      相关资源
      最近更新 更多