如果我执行下面的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列表恢复正常。
问题:
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();
}
}
答案 0 :(得分:6)
当您明确调用inputStream.close()
时,您将更改TCP正常连接释放的顺序。在这种情况下,连接的客户端在从服务器接收FIN数据包之前关闭,从而使套接字处于TIME_WAIT状态。在某些时候,传出连接的所有本地端口都会被这些TIME_WAIT套接字占用,并且不能再建立传出连接。
当您不致电inputStream.close()
时,服务器端会通过outputStream.close()
呼叫关闭连接。客户端套接字有足够的时间从服务器接收FIN,然后在垃圾回收时,它们会被终结器方法正常关闭。
在测试中有两种方法可以解决此问题:
inputStream
读取,直到收到-1,这意味着另一方已启动连接关闭(即收到FIN)。只需在assertEquals(-1, inputStream.read());
inputStream.close();
即可
clientSocket.setSoLinger(true, 0);
inputStream.close()
将强制客户端发送RST并中止连接。