2015-08-12 3 views
5

Wenn ich den JUnit-Test unten OHNE die Zeile "inputStream.close()" (siehe unten) ausführen, können mehr als 60000 Anfragen verarbeitet werden (Ich habe den Prozess dann getötet). Mit dieser Linie, habe ich nicht verwalten mehr als 15.000 Anfragen machen, weil:Client SocketInputStream.close() führt zu mehr Ressourcenverbrauch?

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) 

ich es unter Windows laufen, vor dem Start der Test, den ich für die netstat Liste warten wieder normal zu sein. Fragen

:

  • warum ruft socketInputStream.close() auf der Client-Seite Harms in diesem Fall?
  • oder was ist falsch mit dem Code?

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

Sicher, dass Sie nicht diese von hinten nach vorne haben? – EJP

+0

Können Sie weitere Informationen zu Windows-Version, Plattform 32 oder 64 Bit und Java-Version bereitstellen? – sibnick

+0

Als Referenz habe ich Ihren Test auf Ubuntu 15.04 durchgeführt. Es hat alle Millionen Iterationen ohne Fehler abgeschlossen. Im Zustand TIME_WAIT gab es zu jedem Zeitpunkt während der Ausführung ungefähr 28000 Sockets. (Dies ist aufgrund der Geschwindigkeit der Ausführung und das Öffnen neuer Sockets) – dsh

Antwort

6

Wenn Sie explizit inputStream.close() Sie die Reihenfolge der TCP anmutigen Verbindungsfreigabe ändern aufrufen. In diesem Fall wird die Client-Seite der Verbindung geschlossen, bevor das FIN-Paket vom Server empfangen wird, wodurch der Socket im Zustand TIME_WAIT verbleibt. Irgendwann werden alle lokalen Ports für ausgehende Verbindungen mit diesen TIME_WAIT-Sockets belegt, und es können keine ausgehenden Verbindungen mehr hergestellt werden.

Wenn Sie nicht inputStream.close() anrufen, werden die Verbindungen von der Server-Seite mit outputStream.close() Anruf heruntergefahren. Die Client-Sockets haben genug Zeit, um FIN vom Server zu empfangen, und bei der Speicherbereinigung werden sie durch die Finalizer-Methode ordnungsgemäß geschlossen.

Es gibt zwei Möglichkeiten, das Verfahren in Ihrem Test zu beheben:

  1. Bevorzugte Art und Weise ist von inputStream Lesen fortzusetzen, bis Sie -1 erhalten, was bedeutet, dass die andere Seite der Verbindungsabbau eingeleitet hat (dh FIN Ist angekommen). Legen Sie einfach assertEquals(-1, inputStream.read()); vor inputStream.close();
  2. Die zweite Option durch die gescheiterte Freilassung zu zwingen
    clientSocket.setSoLinger(true, 0);
    In diesem Fall Einstellung inputStream.close() wird Client zwingen RST zu senden und die Verbindung abbrechen.

More about orderly and abortive TCP connection release.

+0

Als Referenz, nur die zweite Option (Force schließen) arbeitete für mich auf Java 8 auf OSX. – gar

Verwandte Themen