2015-08-12 3 views
5

Se eseguo il test JUnit di seguito SENZA la riga "inputStream.close()" (vedi sotto), è possibile elaborare più di 60000 richieste (ho quindi eliminato il processo). Con questa linea, non sono riuscito a fare più di 15000 le richieste, a causa di:Client SocketInputStream.close() porta a un maggiore consumo di risorse?

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) 

ho eseguito su Windows, prima di iniziare il test aspetto l'elenco netstat di essere tornato alla normalità.

Domande:

  • perché sta chiamando socketInputStream.close() sui danni lato client in questo caso?
  • o cosa c'è di sbagliato nel codice?

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

Certo non si dispone di questo nuovo alla parte anteriore? – EJP

+0

È possibile fornire ulteriori informazioni sulla versione di Windows, sulla piattaforma 32 o 64 bit e sulla versione java? – sibnick

+0

Solo per riferimento, ho eseguito il test su Ubuntu 15.04. Ha completato tutti i milioni di iterazioni senza errori. Circa 28000 socket esistevano nello stato TIME_WAIT in qualsiasi momento durante l'esecuzione. (questo è dovuto alla velocità di esecuzione e all'apertura di nuove prese) – dsh

risposta

6

Quando si chiama in modo esplicito inputStream.close() si modifica l'ordine di rilascio TCP collegamento grazioso. In questo caso il lato client della connessione viene chiuso prima di ricevere il pacchetto FIN dal server, lasciando quindi il socket nello stato TIME_WAIT. A un certo punto tutte le porte locali per le connessioni in uscita vengono occupate con questi socket TIME_WAIT e non è possibile effettuare più connessioni in uscita.

Quando non si chiami inputStream.close(), i collegamenti sono chiusi verso il basso dal lato server con outputStream.close() chiamata. I socket client hanno abbastanza tempo per ricevere FIN dal server, e quindi al termine della garbage collection vengono chiusi con grazia dal metodo finalizzatore.

Ci sono due opzioni per risolvere il procedimento nel test:

  1. modo preferibile è quello di continuare a leggere da inputStream fino a ricevere -1, il che significa che l'altra parte ha iniziato l'arresto di collegamento (cioè FIN è ricevuto). Basta inserire assertEquals(-1, inputStream.read()); prima inputStream.close();
  2. La seconda opzione è quella di forzare il rilascio abortiva impostando
    clientSocket.setSoLinger(true, 0);
    In questo caso inputStream.close() costringerà client per inviare RST e interrompere la connessione.

More about orderly and abortive TCP connection release.

+0

Per riferimento, solo la seconda opzione (forza chiusa) ha funzionato per me su Java 8 su OSX. – gar

Problemi correlati