客户端SocketInputStream.close()导致更多的资源消耗?

时间:2015-08-12 13:11:44

标签: java windows performance sockets

如果我执行下面的JUnit测试而不使用“inputStream.close()”行(见下文),则可以处理超过60000个请求(然后我终止了该过程)。有了这一行,我没有管理超过15000个请求,因为:

java.net.SocketException: No buffer space available (maximum connections reached?): connect
    at java.net.PlainSocketImpl.socketConnect(Native Method)
    at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:351)
    at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:213)
    at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:200)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)
    at java.net.Socket.connect(Socket.java:529)
    at java.net.Socket.connect(Socket.java:478)
    at java.net.Socket.<init>(Socket.java:375)
    at java.net.Socket.<init>(Socket.java:189)
    at SocketTest.callServer(SocketTest.java:60)
    at SocketTest.testResourceConsumption(SocketTest.java:52)

我在Windows上运行它,在开始测试之前我等待netstat列表恢复正常。

问题:

  • 为什么在这种情况下在客户端调用socketInputStream.close()会有害?
  • 或代码有什么问题?
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

import junit.framework.TestCase;

public class SocketTest extends TestCase {
    private static final int PORT = 12345;
    private ServerSocket serverSocket;

    public void setUp() throws Exception {
        serverSocket = new ServerSocket(PORT);

        new Thread(new Runnable() {
            @Override
            public void run() {
                while(true) {
                    try {
                        final Socket socket = serverSocket.accept();
                        new Thread(new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    OutputStream outputStream = socket.getOutputStream();
                                    for(int i = 0; i < 100; i++) {
                                        outputStream.write(i);
                                    }
                                    outputStream.close();
                                    // in fact the previous line calls this already:
                                    // socket.close();
                                } catch (IOException e) {
                                    throw new RuntimeException(e);
                                }
                            }
                        }).start();
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }).start();
    }

    public void testResourceConsumption() throws Exception {
        for (int i=0; i<1000000; i++) {
            callServer();
            if (i % 1000 == 0) {
                System.out.println(i);
            }
        }
    }

    private void callServer() throws Exception {
        Socket clientSocket = new Socket("localhost", PORT);
        InputStream inputStream = clientSocket.getInputStream();
        for (int i = 0; i < 100; i++) {
            assertEquals(i, inputStream.read());
        }
        ///////////////// THIS LINE IS INTERESTING 
        inputStream.close();
        // in fact the previous line calls this already:
        // clientSocket.close();
    }

    public void tearDown() throws Exception {
        serverSocket.close();
    }

}

1 个答案:

答案 0 :(得分:6)

当您明确调用inputStream.close()时,您将更改TCP正常连接释放的顺序。在这种情况下,连接的客户端在从服务器接收FIN数据包之前关闭,从而使套接字处于TIME_WAIT状态。在某些时候,传出连接的所有本地端口都会被这些TIME_WAIT套接字占用,并且不能再建立传出连接。

当您不致电inputStream.close()时,服务器端会通过outputStream.close()呼叫关闭连接。客户端套接字有足够的时间从服务器接收FIN,然后在垃圾回收时,它们会被终结器方法正常关闭。

在测试中有两种方法可以解决此问题:

  1. 优选的方法是继续从inputStream读取,直到收到-1,这意味着另一方已启动连接关闭(即收到FIN)。只需在assertEquals(-1, inputStream.read());
  2. 之前插入inputStream.close();即可
  3. 第二种选择是通过设置
    强制中止释放 clientSocket.setSoLinger(true, 0);
    在这种情况下,inputStream.close()将强制客户端发送RST并中止连接。
  4. More about orderly and abortive TCP connection release.