Java BindException:地址已在客户端套接字上使用?

Java BindException:地址已在客户端套接字上使用?,java,sockets,httpclient,Java,Sockets,Httpclient,我有一个客户机-服务器分层体系结构,客户机向服务器发出类似RPC的请求。我使用Tomcat托管servlet,使用ApacheHttpClient向其发出请求 我的代码是这样的: private static final HttpConnectionManager CONN_MGR = new MultiThreadedHttpConnectionManager(); final GetMethod get = new GetMethod(); final HttpCli

我有一个客户机-服务器分层体系结构,客户机向服务器发出类似RPC的请求。我使用Tomcat托管servlet,使用ApacheHttpClient向其发出请求

我的代码是这样的:

    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)
原因:java.net.BindException:地址已在使用中
位于java.net.PlainSocketImpl.socketBind(本机方法)
位于java.net.AbstractPlainSocketImpl.bind(AbstractPlainSocketImpl.java:336)
位于java.net.Socket.bind(Socket.java:588)
位于java.net.Socket(Socket.java:387)
位于java.net.Socket(Socket.java:263)
位于org.apache.commons.httpclient.protocol.DefaultProtocolSocketFactory.createSocket(DefaultProtocolSocketFactory.java:80)
位于org.apache.commons.httpclient.protocol.DefaultProtocolSocketFactory.createSocket(DefaultProtocolSocketFactory.java:122)
位于org.apache.commons.httpclient.HttpConnection.open(HttpConnection.java:707)
位于org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:387)
位于org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171)
位于org.apache.commons.httpclient.httpclient.executeMethod(httpclient.java:397)
位于org.apache.commons.httpclient.httpclient.executeMethod(httpclient.java:323)
有没有关于如何解决这个问题的想法?我猜这与客户端试图重用短暂的客户端端口有关,但是为什么会发生这种情况/我如何修复它?
谢谢

由于每秒有数百个连接,而且不知道您的连接要打开、做它们的事情、关闭和回收多长时间,我怀疑这只是您将遇到的一个问题。您可以做的一件事是在try块中捕获
BindException
,在绑定不成功的情况下使用它做任何您需要做的事情,并将整个调用包装在
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) {
        ...
    }
}
更新问题:一个奇怪的问题:您的
多线程HttpConnectionManager允许的最大总连接数和每主机连接数是多少?在您的代码中,这应该是:

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

因此,您触发的请求超过了允许打开的TCP/IP端口数。我不做HttpClient,所以我不能详细说明这一点,但从理论上讲,对于这个特殊问题有三种解决方案:

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

  • 可以找到关于您遇到的问题的非常好的讨论。在Tomcat端,默认情况下,它将使用该选项,这将允许服务器重用及时等待的套接字。此外,ApacheHTTP客户端将默认使用keep-alives,并尝试重用连接


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

    因此,问题是其他一个HttpClient实例意外地没有使用我实例化的多线程HttpConnectionManager,因此我实际上根本没有速率限制。修复此问题修复了引发的异常


    不过,谢谢你的建议

    即使我们调用HttpClientUtils.closequity(客户端);但是在您的代码中,如果您试图从HttpResponse实体(如InputStream contentStream=HttpResponse.getEntity().getContent())读取内容,那么您也应该关闭InputStream,然后只有HttpClient连接才能正确关闭

    如果有一个更倾向于缺少上述资源的例外,它不会失败吗?大多数资源耗尽错误都来自socket()调用-手册页提到了由于资源不足而导致失败的至少三种不同方式,而绑定手册页没有任何方式。我希望Java异常会指出这一点。你还认为这可能是一个问题吗?如果HttpClient被编码为这样做,它只会在出现这样一个异常的情况下失败——我没有看过
    HttpClient#executeMethod
    ,但根据你的经验,它们似乎没有。有理由认为,他们可能在内部捕获了BindException并在不同的客户端端口上重试连接,但这会打开一整罐蠕虫——他们重试了多少次?他们怎么知道你想这么做他们抛出BindException并由您决定也是有道理的。通过查看跟踪,您可以看到它是直接从本机套接字连接方法抛出的。Steven,但是我的观点是,
    HttpClient
    可以捕获抛出的异常并对其做些处理--我已经说明了一些注意事项,仍然很难决定它应该尝试多少次重试,等等,期望
    HttpClient
    只允许异常传递,以便您自己决定如何处理它也是合理的;我只是指出“您触发的请求超过了允许打开的TCP/IP端口数”的评论与我收到的错误不匹配。每台主机:2,最多:20。我怎么能在如此保守的默认值下超过任何套接字限制…:-P