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

时间:2012-02-29 19:26:51

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

}

7 个答案:

答案 0 :(得分:24)

在处理完回复后,听起来好像没有消耗实体。确保将以下代码放在finally块中:

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

我建议你阅读HttpClient Tutorial

答案 1 :(得分: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;
    }

}

在另一堂课......

public static void safeClose(HttpClient client)
{
    if(client != null && client.getConnectionManager() != null)
    {
        client.getConnectionManager().shutdown();
    }
}

答案 2 :(得分:6)

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

您可以通过阅读response.getEntity()全部来解决此问题。

答案 3 :(得分:3)

我以为我会详细说明其他答案。我也遇到过这个问题。问题是因为我没有消费内容。

如果您没有,那么连接将保持连接,并且您无法使用此相同连接发送新请求。对我来说,当我使用android中提供的BasicResponseHandler时,发现这是一个特别困难的错误。代码看起来像这样......

public String handleResponse(final HttpResponse response)
            throws HttpResponseException, IOException {
        StatusLine statusLine = response.getStatusLine();
        if (statusLine.getStatusCode() >= 300) {
            throw new HttpResponseException(statusLine.getStatusCode(),
                    statusLine.getReasonPhrase());
        }

        HttpEntity entity = response.getEntity();
       return entity == null ? null : EntityUtils.toString(entity);
    }

因此,如果状态行高于300,那么我不会使用内容。在我的案例中有内容。我这样做了自己的课......

public class StringHandler implements ResponseHandler<String>{

    @Override
    public BufferedInputStream handleResponse(HttpResponse response) throws IOException {
    public String handleResponse(final HttpResponse response)
                throws HttpResponseException, IOException {
            StatusLine statusLine = response.getStatusLine();
           HttpEntity entity = response.getEntity();
            if (statusLine.getStatusCode() >= 300) {
                if (entity != null) {
                    entity.consumeContent();
                }
                throw new HttpResponseException(statusLine.getStatusCode(),
                        statusLine.getReasonPhrase());
            }


           return entity == null ? null : EntityUtils.toString(entity);
        }
    }

}

所以基本上消耗内容!

答案 4 :(得分:1)

我遇到过同样的问题。我正在消费所有内容。

我发现如果我在发出请求后进行垃圾收集,一切正常,无需关闭并创建新的AndroidHttpClient:

的System.gc();

答案 5 :(得分:1)

问题解决方案就足够了(我有同样的想法):

EntityUtils.consume(response.getEntity());

消费内部执行空检查

答案 6 :(得分:1)

由于这些答案中的许多都是陈旧的,并且取决于现在被删除的consumeContent()方法,我认为我会回答Timeout waiting for connection from pool的问题。

    HttpEntity someEntity =  response.getEntity();

    InputStream stream = someEntity.getContent();
    BufferedReader rd = new BufferedReader(new InputStreamReader(stream));

    StringBuffer result = new StringBuffer();
    String line = "";
    while ((line = rd.readLine()) != null) {
        result.append(line);
    }
    // On certain android OS levels / certain hardware, this is not enough.
    stream.close(); // This line is what is recommended in the documentation

以下是文档中显示的内容:

cz.msebera.android.httpclient.HttpEntity
@java.lang.Deprecated 
public abstract void consumeContent()
                            throws java.io.IOException
This method is deprecated since version 4.1. Please use standard java
convention to ensure resource deallocation by calling
InputStream.close() on the input stream returned by getContent()