BindException:客户端套接字上已经使用的地址?

时间:2009-12-30 02:11:41

标签: java sockets httpclient

我有一个客户端 - 服务器分层架构,客户端向服务器发出类似RPC的请求。我正在使用Tomcat来托管servlet,并使用Apache HttpClient向它发出请求。

我的代码是这样的:

    private static final HttpConnectionManager CONN_MGR = new MultiThreadedHttpConnectionManager();
    final GetMethod get = new GetMethod();
    final HttpClient httpClient = new HttpClient(CONN_MGR);
    get.getParams().setCookiePolicy(CookiePolicy.IGNORE_COOKIES);
    get.getParams().setParameter(HttpMethodParams.USER_AGENT, USER_AGENT);

    get.setQueryString(encodedParams);
    int responseCode;
    try {
        responseCode = httpClient.executeMethod(get);
    } catch (final IOException e) {
        ...
    }
    if (responseCode != 200)
        throw new Exception(...);

    String responseHTML;
    try {
        responseHTML = get.getResponseBodyAsString(100*1024*1024);
    } catch (final IOException e) {
        ...
    }
    return responseHTML;

它在轻负载的环境中运行良好,但是当我每秒发出数百个请求时,我开始看到这个 -

Caused by: java.net.BindException: Address already in use
    at java.net.PlainSocketImpl.socketBind(Native Method)
    at java.net.AbstractPlainSocketImpl.bind(AbstractPlainSocketImpl.java:336)
    at java.net.Socket.bind(Socket.java:588)
    at java.net.Socket.<init>(Socket.java:387)
    at java.net.Socket.<init>(Socket.java:263)
    at org.apache.commons.httpclient.protocol.DefaultProtocolSocketFactory.createSocket(DefaultProtocolSocketFactory.java:80)
    at org.apache.commons.httpclient.protocol.DefaultProtocolSocketFactory.createSocket(DefaultProtocolSocketFactory.java:122)
    at org.apache.commons.httpclient.HttpConnection.open(HttpConnection.java:707)
    at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:387)
    at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171)
    at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)
    at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323)

有关如何解决此问题的任何想法?我猜这与客户试图重用短暂的客户端端口有关,但为什么会发生这种情况/如何解决? 谢谢!

5 个答案:

答案 0 :(得分:2)

可以找到您遇到的问题的非常好的讨论here。在Tomcat端,默认情况下它将使用SO_REUSEADDR选项,这将允许服务器重用TIME_WAIT中的套接字。此外,Apache http客户端默认使用keep-alives,并尝试重用连接。

您的问题似乎是由于未在HttpClient上调用 releaseConnection 引起的。这是连接重复使用所必需的。否则,连接将保持打开,直到垃圾收集器到来并关闭它,或者服务器断开保持活动状态。在这两种情况下,它都不会返回池中。

答案 1 :(得分:1)

每秒有数百个连接,并且不知道你的连接保持打开多长时间,做他们的事情,关闭,并获得回收,我怀疑这只是你将要遇到的问题。您可以做的一件事就是捕获try块中的BindException,使用它来执行在bind-unsuccessful情况下需要做的任何事情,并将整个调用包装在依赖于while的循环中指示绑定是否成功的标志。在我的头顶:

boolean hasBound = false;
while (!hasBound) {
    try {
        hasBound = true;
        responseCode = httpClient.executeMethod(get);
    } catch (BindException e) {
        // do anything you want in the bound-unsuccessful case
    } catch (final IOException e) {
        ...
    }
}

更新问题:一个奇怪的问题:MultiThreadedHttpConnectionManager允许的最大总连接数和每个主机连接数是多少?在你的代码中,那就是:

CONN_MGR.getParams().getDefaultMaxConnectionsPerHost();
CONN_MGR.getParams().getMaxTotalConnections();

答案 2 :(得分:0)

因此,您发出的请求多于允许打开TCP / IP端口的请求。我不做HttpClient,所以我不能详细讨论这个问题,但理论上对于这个特殊问题有三种解决方案:

  1. 基于硬件:添加另一个NIC(网络接口卡)。
  2. 基于软件:使用后直接关闭连接和/或增加连接超时。
  3. 基于平台:增加允许打开的TCP / IP端口数量。可能是特定于操作系统和/或NIC驱动程序特定的。绝对最大值为65535,其中几个可能已经被保留/使用(例如端口80)。

答案 3 :(得分:0)

所以事实证明问题是其他HttpClient实例之一意外地没有使用我实例化的MultiThreadedHttpConnectionManager,所以我实际上根本没有速率限制。修复此问题修复了抛出的异常。

感谢所有的建议!

答案 4 :(得分:0)

即使我们调用HttpClientUtils.closeQuietly(client);但是在你的代码中,如果试图从HttpResponse实体读取内容,比如InputStream contentStream = HttpResponse.getEntity()。getContent(),那么你应该关闭输入流,然后才能正确关闭HttpClient连接。