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

bou*_*ert 5 java sockets windows performance

如果我在下面执行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)
Run Code Online (Sandbox Code Playgroud)

我在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();
    }

}
Run Code Online (Sandbox Code Playgroud)

apa*_*gin 6

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

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

在测试中有两种方法可以修复该过程:

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

有关有序和无效的TCP连接释放的更多信息.