android httpclient挂起第二次请求到服务器(连接超时)

Nor*_*ain 19 android httpclient

我正在努力解决以下问题:我的应用程序使用HttpClient向http服务器发出一系列请求.我使用HttpPut将数据发送到服务器.第一个请求进行得很快,第二个请求挂起40秒,然后我抓住Connection超时异常.我正在尝试重用我的HttpClient并通过同一个实例发送第二个请求.如果我与新的ConnectionManager一起创建新的HttpClient,那么一切正常.

为什么会这样?以及如何修复它并且每次都不创建新的HttpClient?

提前致谢.

这是我的代码:(如果我在doPut中评论readClient = newHttpClient(readClient),那么问题就出现了.

public class WebTest
{
private HttpClient readClient;
private SchemeRegistry httpreg;
private HttpParams params;

private URI url; //http://my_site.net/data/

protected HttpClient newHttpClient(HttpClient oldClient)
{
    if(oldClient != null)
        oldClient.getConnectionManager().shutdown();

    ClientConnectionManager cm = new SingleClientConnManager(params, httpreg);
    return new DefaultHttpClient(cm, params);
}

protected String doPut(String data)
{
    //****************************
    //Every time we need to send data, we do new connection
    //with new ConnectionManager and close old one
    readClient = newHttpClient(readClient);

    //*****************************


    String responseS = null;
    HttpPut put = new HttpPut(url);
    try
    {
        HttpEntity entity = new StringEntity(data, "UTF-8");
        put.setEntity(entity);
        put.setHeader("Content-Type", "application/json; charset=utf-8");
        put.setHeader("Accept", "application/json");
        put.setHeader("User-Agent", "Apache-HttpClient/WebTest");

        responseS = readClient.execute(put, responseHandler);
    }
    catch(IOException exc)
    {
        //error handling here
    }
    return responseS;
}

public WebTest()
{
    httpreg = new SchemeRegistry();
    Scheme sch = new Scheme("http", PlainSocketFactory.getSocketFactory(), 80);
    httpreg.register(sch);

    params = new BasicHttpParams();
    ConnPerRoute perRoute = new ConnPerRouteBean(10);
    ConnManagerParams.setMaxConnectionsPerRoute(params, perRoute);
    ConnManagerParams.setMaxTotalConnections(params, 50);
    ConnManagerParams.setTimeout(params, 15000);
    int timeoutConnection = 15000;
    HttpConnectionParams.setConnectionTimeout(params, timeoutConnection);
    // Set the default socket timeout (SO_TIMEOUT) 
    // in milliseconds which is the timeout for waiting for data.
    int timeoutSocket = 40000;
    HttpConnectionParams.setSoTimeout(params, timeoutSocket);
}

private ResponseHandler<String> responseHandler = new ResponseHandler<String>() 
{
    @Override
    public String handleResponse(HttpResponse response)
            throws ClientProtocolException, IOException
    {
        StatusLine statusLine = response.getStatusLine();
        if (statusLine.getStatusCode() >= 300) 
        {
            throw new HttpResponseException(statusLine.getStatusCode(),
                    statusLine.getReasonPhrase());
        }

        HttpEntity entity = response.getEntity();
        if(entity == null)
            return null;

        InputStream instream = entity.getContent();
        return this.toString(entity, instream, "UTF-8");
    }

    public String toString(
            final HttpEntity entity, 
            final InputStream instream, 
            final String defaultCharset) throws IOException, ParseException 
    {
        if (entity == null) 
        {
            throw new IllegalArgumentException("HTTP entity may not be null");
        }

        if (instream == null) 
        {
            return null;
        }
        if (entity.getContentLength() > Integer.MAX_VALUE) 
        {
            throw new IllegalArgumentException("HTTP entity too large to be buffered in memory");
        }
        int i = (int)entity.getContentLength();
        if (i < 0) 
        {
            i = 4096;
        }
        String charset = EntityUtils.getContentCharSet(entity);
        if (charset == null) 
        {
            charset = defaultCharset;
        }
        if (charset == null) 
        {
            charset = HTTP.DEFAULT_CONTENT_CHARSET;
        }

        Reader reader = new InputStreamReader(instream, charset);

        StringBuilder buffer=new StringBuilder(i);
        try 
        {
            char[] tmp = new char[1024];
            int l;
            while((l = reader.read(tmp)) != -1) 
            {
                buffer.append(tmp, 0, l);
            }
        } finally 
        {
            reader.close();
        }

        return buffer.toString();
    }
}; 
Run Code Online (Sandbox Code Playgroud)

}

nee*_*vek 24

听起来在完成处理响应后你不会使用实体.确保将以下代码放在finally块中:

if (httpEntity != null) {
    try {
        httpEntity.consumeContent();
    } catch (IOException e) {
        Log.e(TAG, "", e);
    }
}
Run Code Online (Sandbox Code Playgroud)

我建议你阅读HttpClient教程.

  • 这应该是正确答案!问题解决了! (2认同)

Ric*_*ich 13

听起来很奇怪,但我遇到了同样的问题.我正在处理的应用程序是连续几次请求下载一堆缩略图以在ListView中显示,在第二个之后,它会挂起,好像HttpClient代码中有一个死锁.

我发现的奇怪修复是使用AndroidHttpClient而不是DefaultHttpClient.我一做到这一点,并且在走这条路线之前尝试了很多东西,它开始工作得很好.只需记住在完成请求后调用client.close().

AndroidHttpClient在文档中描述为DefaultHttpClient,具有"合理的默认设置和Android注册方案".由于这是在api level 8(Android 2.2)中引入的,我挖出源来复制这些"默认设置",这样我就可以比api级别更进一步使用它.这是我的复制默认值的代码和一个带有静态方法的辅助类,用于安全地关闭它

public class HttpClientProvider {

    // Default connection and socket timeout of 60 seconds. Tweak to taste.
    private static final int SOCKET_OPERATION_TIMEOUT = 60 * 1000;

    public static DefaultHttpClient newInstance(String userAgent)
    {
        HttpParams params = new BasicHttpParams();

        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
        HttpProtocolParams.setContentCharset(params, HTTP.DEFAULT_CONTENT_CHARSET);
        HttpProtocolParams.setUseExpectContinue(params, true);

        HttpConnectionParams.setStaleCheckingEnabled(params, false);
        HttpConnectionParams.setConnectionTimeout(params, SOCKET_OPERATION_TIMEOUT);
        HttpConnectionParams.setSoTimeout(params, SOCKET_OPERATION_TIMEOUT);
        HttpConnectionParams.setSocketBufferSize(params, 8192);

        SchemeRegistry schReg = new SchemeRegistry();
        schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        schReg.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
        ClientConnectionManager conMgr = new ThreadSafeClientConnManager(params, schReg);

        DefaultHttpClient client = new DefaultHttpClient(conMgr, params);

        return client;
    }

}
Run Code Online (Sandbox Code Playgroud)

在另一堂课......

public static void safeClose(HttpClient client)
{
    if(client != null && client.getConnectionManager() != null)
    {
        client.getConnectionManager().shutdown();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 如果您在每次请求时重新创建httpClient并在此之后关闭它,为什么使用ThreadSafeClientConnManager而不是SingleClientConnManager? (4认同)
  • 如果你连续尝试3次,这有用吗?此解决方案(每次都不创建新对象)适用于2个请求,但不适用于3个请求. (2认同)
  • 这不是问题的解决方案,这是一种解决方法.您可以而且应该重用http客户端对象.下面neevek发布的答案对我有用(通过调用entity.consumeContent()来使用HttpResponse实体). (2认同)
  • 我正在阅读一个实时,无限的流.consumeContent无限期阻塞,似乎坚持尝试重用连接.关闭connectionManager的三个欢呼声.适合我! (2认同)

wil*_*dev 6

在循环中执行多个请求时,我遇到了同样的麻烦.

您可以通过阅读解决这一切response.getEntity().