2015-08-12 2 views
5

Если я выполнил тест JUnit ниже БЕЗ строки «inputStream.close()» (см. Ниже), можно обработать более 60000 запросов (тогда я убил процесс). С этой линии, мне не удалось сделать более 15000 запросов, из-за:Клиент SocketInputStream.close() приводит к увеличению потребления ресурсов?

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

} 
+0

Конечно, вы не должны это задом наперед? – EJP

+0

Можете ли вы предоставить дополнительную информацию о версии Windows, платформе 32 или 64 бит и java-версии? – sibnick

+0

Просто для справки, я проверил ваш тест на Ubuntu 15.04. Он выполнил все миллионы итераций без ошибок. Примерно 28000 сокетов существовали в состоянии TIME_WAIT в любой момент времени во время выполнения. (это связано со скоростью выполнения и открытием новых сокетов) – dsh

ответ

6

Когда вы явно вызвать inputStream.close() изменить порядок TCP изящная разрывать соединение. В этом случае клиентская сторона соединения закрывается до получения пакета FIN с сервера, оставляя сокет в состоянии TIME_WAIT. В какой-то момент все локальные порты для исходящих соединений становятся занятыми этими гнездами TIME_WAIT, и больше не может быть сделано исходящих соединений.

Когда вы не вызываете inputStream.close(), соединения завершаются на стороне сервера с помощью вызова outputStream.close(). У сокетов клиента достаточно времени для получения FIN с сервера, а затем во время сбора мусора они изящно закрываются методом finalizer.

Есть два варианта, чтобы исправить процедуру в тесте:

  1. Предпочитаемый способ является продолжить чтение inputStream, пока вы не получите -1, что означает, что другая сторона инициировала закрытие соединения (т.е. FIN получен). Просто вставьте assertEquals(-1, inputStream.read()); перед тем inputStream.close();
  2. Второй вариант, чтобы заставить неудачную релиз, установив
    clientSocket.setSoLinger(true, 0);
    В этом случае inputStream.close() заставит клиента посылать RST и прервать соединение.

More about orderly and abortive TCP connection release.

+0

Для справки, только вторая опция (принудительное закрытие) работала для меня на Java 8 на OSX. – gar

Смежные вопросы