【问题标题】:HttpURLConnection: BindException when creating many connectionsHttpURLConnection:创建多个连接时出现BindException
【发布时间】:2014-11-15 20:49:11
【问题描述】:

出于测试/基准测试的目的,我想编写一个循环执行以下任务的 Java 程序:

  1. 通过 HTTP GET 从服务器加载数据
  2. (根据收到的数据生成答案 - 此时并不重要)
  3. 通过 HTTP POST 将答案发送到同一服务器

这个循环同时在多个线程上运行。

启动后,程序可以在短时间内正常运行,并且每线程每秒能够执行约 300 个循环(网络服务器在同一台机器上运行)。但 5-7 秒后,我收到了BindException: Address already in use

在 20-30 秒的冷却时间后重新启动程序会导致相同的行为;当我立即重新启动它而不等待时,它会立即崩溃......所以我想这可能是绑定资源的问题。

使用HttpURLConnection 是一种快速而肮脏的方法。相关部分:

从网络服务器获取数据

public String fetchData() throws IOException {

        HttpURLConnection conn = (HttpURLConnection)url.openConnection();
        conn.setUseCaches(false);
        conn.setRequestMethod("GET");

        BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        String inputLine;
        StringBuffer response = new StringBuffer();

        while ((inputLine = in.readLine()) != null) {
            response.append(inputLine);
        }

        in.close();
        conn.disconnect();

        return response.toString();
    }

发送答案

public void sendData(byte[] data) throws IOException {

        HttpURLConnection conn = (HttpURLConnection)url.openConnection();
        conn.setUseCaches(false);
        conn.setRequestMethod("POST");
        conn.setDoOutput(true);

        OutputStream os = conn.getOutputStream();
        os.write(data);    
        os.close();
        conn.disconnect();
    }

在线程内调用这两个方法

@Override
public void run() {
    while(true) {
        try {
            String data = fetchData();
            String answer = // ... generating answer
            sendData(answer.getBytes("UTF-8"));
        } catch (IOException e) {
            // ...
        }
    }
}

线程之间没有共享一个 URL 对象 - 每个线程都有自己的 URL 实例(但是,每个实例都指向同一个地址)。

编辑:

这是发生的第一个异常的堆栈跟踪:

java.net.BindException: Address already in use: connect
    at java.net.DualStackPlainSocketImpl.connect0(Native Method)
    at java.net.DualStackPlainSocketImpl.socketConnect(Unknown Source)
    at java.net.AbstractPlainSocketImpl.doConnect(Unknown Source)
    at java.net.AbstractPlainSocketImpl.connectToAddress(Unknown Source)
    at java.net.AbstractPlainSocketImpl.connect(Unknown Source)
    at java.net.PlainSocketImpl.connect(Unknown Source)
    at java.net.SocksSocketImpl.connect(Unknown Source)
    at java.net.Socket.connect(Unknown Source)
    at java.net.Socket.connect(Unknown Source)
    at sun.net.NetworkClient.doConnect(Unknown Source)
    at sun.net.www.http.HttpClient.openServer(Unknown Source)
    at sun.net.www.http.HttpClient.openServer(Unknown Source)
    at sun.net.www.http.HttpClient.<init>(Unknown Source)
    at sun.net.www.http.HttpClient.New(Unknown Source)
    at sun.net.www.http.HttpClient.New(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.plainConnect(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.connect(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
    at PayloadProcessor.fetchData(PayloadProcessor.java:66)
    at PayloadProcessor.run(PayloadProcessor.java:32)
    at java.lang.Thread.run(Unknown Source)

它出现在 fetchdata 方法的下面一行:

in = new BufferedReader(new InputStreamReader(conn.getInputStream()));

我删除了对 disconnect() 的调用(正如 Aaron 指出的那样) - 不幸的是,同样的问题仍然存在。


让我们假设一切都正确关闭(不确定是否是这种情况,只是假设它) - 可能是程序太快了吗?我在 stackoverflow 上找到了另一个 post,“解决方案”是添加一个简单的 Thread.sleep - 但由于它是一个基准测试并且应该尽可能快地运行,我不喜欢这样。负载测试工具如何处理这个问题?


我将线程数减少到1,即使这样问题仍然存在。

【问题讨论】:

  • 在这里发布错误的完整堆栈跟踪可能很有用。无论如何,我向您推荐的第一件事是将 disconnect 调用包含在 finally 子句中:可能出现了太多异常,以至于连接没有被正确关闭,导致泄漏。
  • 谢谢,我添加了堆栈跟踪。你是对的,确实有很多异常 - 但是出于调试目的,我现在在第一个异常引发后终止程序(上面的代码中没有显示)并且仍然有同样的问题。第一个异常已经是 BindException。
  • 请在您的操作系统中添加命令netstat -anpt | grep &lt;ip_of_server_your_connected&gt; 或其模拟的结果。
  • 您与一台服务器有多个连接。 Web 服务器(或 servlet 容器)有连接限制。你用什么服务器?

标签: java multithreading sockets http networking


【解决方案1】:

听起来您的本地端口可能用完了。是否有可能并行生成的连接太多,并且释放资源的速度不够快,以供其他线程重用?

我会摆脱对disconnect() 的调用,因为这几乎会为您禁用连接池。来自文档:

表示在不久的将来不太可能向服务器发出其他请求。调用 disconnect() 不应暗示此 HttpURLConnection 实例可用于其他请求。

【讨论】:

  • 谢谢。我删除了对disconnect() 的两个调用,但不幸的是它没有改变任何东西。我同意您对缺少端口的看法,但我不知道为什么会发生这种情况。程序是不是太快了?查看我的编辑
  • 嗯...肯定有问题。你能告诉我们你的catch块里有什么吗?
【解决方案2】:

我认为您用完了本地端口来检查这个: 运行你的程序并运行 netstat 命令。 尝试使用连接池进行 http 连接 HTTP connection pooling using HttpClient

【讨论】:

  • 我还建议使用 HttpClient 而不是直接的 HttpURLConnection。直接使用 HttpURLConnection 会出现各种可怕的问题,HttpClient 会为您处理。
【解决方案3】:

看起来您的本地端口用完了。为防止这种情况,您必须确保重复使用您的连接。 Java 会尝试do this for you,但您需要阅读每个请求的整个响应。

虽然fetchData 函数会读取响应,但sendData 不会。虽然您在这里不需要响应正文,但无论如何您都应该阅读它(直到最后),然后关闭相应的InputStream。那么这个连接可能会被重用。

也不要disconnect你的联系人。

This question 可能会有所帮助。

【讨论】:

    【解决方案4】:

    简单的解决方案只是在回答之前引入延迟。

    String data = fetchData(conn);
    String answer = // ... generating answer
    Thread.sleep(1000);
    sendData(conn, answer.getBytes("UTF-8"));
    

    【讨论】:

      【解决方案5】:

      要获取和发送数据,您需要为每个fetchDatasendData 调用创建一个HttpURLConnection。这意味着,您与服务器的连接太多。正如已经指出的那样,这将使您所有可用的本地端口号用完。

      只需为每个线程创建一个HttpURLConnection。我强烈建议你重构给定的代码如下:

      @Override
      public void run() {
          HttpURLConnection conn = (HttpURLConnection)url.openConnection();
          while(true) {
              try {
                  String data = fetchData(conn);
                  String answer = // ... generating answer
                  sendData(conn, answer.getBytes("UTF-8"));
              } catch (IOException e) {
                  // ...
              }
          }
      }
      

      也就是说,您在 while 循环之外创建一次 HttpURLConnection 对象,并将其作为参数传递给 fetchDatasendData

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-09-07
        • 1970-01-01
        • 2012-04-16
        • 1970-01-01
        相关资源
        最近更新 更多