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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1978053/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 18:47:25  来源:igfitidea点击:

BindException: address already in use on a client socket?

javasocketshttpclient

提问by Steven Schlansker

I've got a client-server tiered architecture with the client making RPC-like requests to the server. I'm using Tomcat to host the servlets, and the Apache HttpClient to make requests to it.

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

My code goes something like this:

我的代码是这样的:

    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;

It works great in a lightly-loaded environment, but when I'm making hundreds of requests per second I start to see this -

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

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)

Any thoughts on how to fix this? I'm guessing it's something to do with the client trying to reuse the ephemeral client ports, but why is this happening / how can I fix it? Thanks!

关于如何解决这个问题的任何想法?我猜这与客户端尝试重用临时客户端端口有关,但为什么会发生这种情况/我该如何解决?谢谢!

采纳答案by Steven Schlansker

So it turns out the problem was that one of the other HttpClient instances accidentally wasn't using the MultiThreadedHttpConnectionManager I instantiated, so I effectively had no rate limiting at all. Fixing this problem fixed the exception being thrown.

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

Thanks for all the suggestions, though!

不过还是谢谢大家的建议!

回答by brianegge

A very good discussion of the problem you are running into can be found here. On the Tomcat side, by default it will use the SO_REUSEADDRoption, which will allow the server to reuse sockets which are in TIME_WAIT. Additionally, the Apache http client will by default use keep-alives, and attempt to reuse connections.

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

Your problems seems to be caused by not calling releaseConnectionon the HttpClient. This is required in order for the connection to be reused. Otherwise, the connection will remain open until garbage collector comes and closes it, or the server disconnects the keep-alive. In both cases, it won't be returned to the pool.

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

回答by delfuego

With hundreds of connections a second, and without knowing how long your connections keep to open, do their thing, close, and get recycled, I suspect that this is just a problem you're going to have. One thing you can do is catch the BindExceptionin your try block, use that to do anything you need to do in the bind-unsuccessful case, and wrap the whole call in a whileloop that depends on a flag indicating whether the bind succeeded. Off the top of my head:

每秒有数百个连接,并且不知道您的连接保持打开、执行、关闭和回收的时间,我怀疑这只是您将要遇到的问题。您可以做的一件事是BindException在 try 块中捕获 ,在绑定不成功的情况下使用它来执行您需要执行的任何操作,并将整个调用包装在一个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) {
        ...
    }
}

Update with question:One curious question: what are the maximum total and per-host number of connections allowed by your MultiThreadedHttpConnectionManager? In your code, that'd be:

更新问题:一个奇怪的问题:您的MultiThreadedHttpConnectionManager? 在您的代码中,这将是:

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

回答by BalusC

Thus, you've fired more requests than TCP/IP ports are allowed to be opened. I don't do HttpClient, so I can't go in detail about this, but in theory there are three solutions for this particular problem:

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

  1. Hardware based: add another NIC (network interface card).
  2. Software based: close connections directly after use and/or increase the connection timeout.
  3. Platform based: increase the amount of TCP/IP ports which are allowed to be opened. May be OS-specific and/or NIC driver-specific. The absolute maximum is 65535, of which several may already be reserved/in use (e.g. port 80).
  1. 基于硬件:添加另一个 NIC(网络接口卡)。
  2. 基于软件:使用后直接关闭连接和/或增加连接超时。
  3. 基于平台:增加允许打开的 TCP/IP 端口数量。可能是特定于操作系统和/或特定于 NIC 驱动程序的。绝对最大值为 65535,其中一些可能已被保留/正在使用(例如端口 80)。

回答by Ankireddy Polu

Even though we invoke HttpClientUtils.closeQuietly(client); but in your code in case trying to read the content from HttpResponse entity like InputStream contentStream = HttpResponse.getEntity().getContent(), then you should close the inputstream also then only HttpClient connection get closed properly.

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