HttpURLConnection:创建许多连接时出现BindException

cer*_*ran 8 java sockets networking multithreading http

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

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

此循环同时在多个线程上运行.

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

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

这是一种快速而肮脏的方法HttpURLConnection.相关部分:

从Web服务器获取数据

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();
    }
Run Code Online (Sandbox Code Playgroud)

发送答案

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();
    }
Run Code Online (Sandbox Code Playgroud)

在线程内部调用两个方法

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

线程之间没有共享的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)
Run Code Online (Sandbox Code Playgroud)

它出现在以下行的fetchdata方法中:

in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
Run Code Online (Sandbox Code Playgroud)

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


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


我将线程数量减少到1,即使这样也会出现问题.

Aar*_*ron 2

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

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

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