如果我执行下面没有“inputStream.close()”行的 JUnit 测试(见下文),可以处理超过 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();
}
}
最佳答案
当您显式调用 inputStream.close()
时,您更改了 TCP 正常连接释放的顺序。在这种情况下,连接的客户端在从服务器接收到 FIN 数据包之前关闭,从而使套接字处于 TIME_WAIT 状态。在某些时候,所有用于传出连接的本地端口都被这些 TIME_WAIT 套接字占用,并且无法建立更多的传出连接。
当您不调用 inputStream.close()
时,服务器端将使用 outputStream.close()
调用关闭连接。客户端套接字有足够的时间从服务器接收 FIN,然后在垃圾回收时它们会被终结器方法优雅地关闭。
有两个选项可以修复测试中的过程:
- 更好的方法是继续从
inputStream
读取,直到收到-1,这意味着对方已经发起连接关闭(即收到FIN)。只需在inputStream.close();
之前插入 - 第二个选项是通过设置强制中止释放
clientSocket.setSoLinger(true, 0);
在这种情况下,inputStream.close()
将强制客户端发送 RST 并中止连接。
assertEquals(-1, inputStream.read());
关于java - Client SocketInputStream.close() 导致更多的资源消耗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31966291/