2012-05-13 8 views
9

Ich schreibe gerade naiven Netzwerk Code für ein Projekt und ein Kumpel wies mich auf die Möglichkeit, dass, wenn ich ein Paket von Informationen vom Server an alle Clients in einer iterativen Weise senden würde ich starke Verzögerung bekommen könnte Clients reagiert nicht richtig.Blockiert ein OutputStream in Java? (Sockets)

Er ist bekannt für Trolling, also war ich skeptisch bei der Implementierung eines sekundären Threads, der jetzt verantwortlich ist, Daten an einen Client zu senden, mit einer Warteschlange, die der Server einfach die Pakete hinzufügt, die dann vom Thread gelesen wird um Daten zu senden.

Die Frage, die ich jetzt habe, nachdem ich darüber nachgedacht habe, ist Wetter oder nicht der OutputStream eines Java-Sockets reiht tatsächlich die Sachen ein, die er selbst senden möchte, wodurch die Notwendigkeit einer Warteschlange vorher beseitigt wird. Die Möglichkeit, dass schwerwiegende Probleme auftreten, tritt nur auf, wenn der Server blockiert, solange er keine Antwort von einem Client erhält, dass das gesendete Objekt empfangen wurde.

Danke.

+0

Ich bin in ein ähnliches Problem geraten, wenn ich versuche, den Ausgangs- oder Eingabestrom in der Lauffunktion eines Threads zu erhalten.Das Problem mit der Blockierung (für getInputStream und getOutputStream) des Sockets liegt darin, dass es sich in der run-Funktion befindet ... die Lösung scheint darin zu bestehen, es in den Konstruktor zu stellen, die Variable zu speichern und dann auf die Variable im run zu verweisen. – Zimm3r

Antwort

4

Wenn Sie in einen Socket schreiben, wird dieser Schreibvorgang natürlich gepuffert. Socket-Objekt haben eine setSendBufferSize() Methode zum Festlegen dieser Puffergröße. Wenn Ihr Schreibvorgang in diesem Puffer zwischengespeichert werden kann, können Sie natürlich sofort auf dem folgenden Socket iterieren. Andernfalls muss dieser Puffer sofort zum Client geleert werden. Während des Spülens werden Sie also blockiert. Wenn Sie verhindern wollen, dass Sie während des Spülens des Puffers blockiert werden, müssen Sie eine SocketChannel in nicht blockierender E/A verwenden. Wie auch immer, die beste Option für das gleichzeitige Schreiben auf mehrere Sockets besteht darin, jeden Socket mit einem anderen Thread zu verwalten, so dass alle Schreibvorgänge gleichzeitig ausgeführt werden können.

+5

"Andernfalls muss dieser Puffer sofort zum Client geleert werden" ist keine korrekte Art, ihn zu schreiben. Der Puffer wird ständig asynchron in das Netzwerk geschrieben, aber er kann nicht gesendet werden, bis im Empfangspuffer des Peers noch Platz ist. Wenn der Peer nicht liest oder langsam liest, füllt sich sein Empfangspuffer, so dass der Sendepuffer voll wird, sodass Ihre nachfolgenden Schreibvorgänge blockiert werden. – EJP

+0

Obwohl Tomasz Post ein besseres Beispiel war, ist dies genau das, was ich in Bezug auf Erklärung EJP suchte. Vielen Dank. – salbeira

8

Ihr Freund hat Recht, aber es hat mehr damit zu tun, wie Protokoll funktioniert. Weitgehend vereinfachte Pakete, die an den Client gesendet werden, müssen bestätigt werden. Wenn der Client nicht antwortet (eingehende Daten nicht lesen kann, der Computer stark ausgelastet ist usw.), empfängt der Server keine Bestätigungen und sendet keine Daten mehr. Dieser in TCP/IP integrierte Mechanismus verhindert, dass ein Ende der Kommunikation große Datenmengen sendet, ohne sicherzustellen, dass das andere Ende sie empfängt. Dies vermeidet die Notwendigkeit, große Datenmengen erneut zu senden.

In Java manifestiert sich dies als blockierendes Schreiben an OutputStream. Der zugrunde liegende TCP/IP-Stack/das Betriebssystem ermöglicht nicht das Senden von mehr Daten, bis der Client bereit ist, sie zu empfangen.

Sie können dies leicht testen! Ich implementiert einfachen Server, die Verbindung akzeptiert, aber nicht eingehende Daten zu lesen:

new Thread(new Runnable() { 
    @Override 
    public void run() { 
     try { 
      final ServerSocket serverSocket = new ServerSocket(4444); 
      final Socket clientSocket = serverSocket.accept(); 
      final InputStream inputStream = clientSocket.getInputStream(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 

    } 
}).start(); 

Und einen einfachen Client, der nur so viele Daten wie es in 4K Chargen sendet:

final Socket client = new Socket("localhost", 4444); 
final OutputStream outputStream = client.getOutputStream(); 
int packet = 0; 
while(true) { 
    System.out.println(++packet); 
    outputStream.write(new byte[1024 * 4]); 
} 

Die Client-Schleife hängt an mein Computer nach 95 Iterationen (Ihre Laufleistung kann variieren). Aber wenn ich von inputStream in Server-Thread lesen - die Schleife geht weiter und weiter.

+0

Ich bin auf ein ähnliches Problem gestoßen, wenn ich versuche, den Ausgabe- oder Eingabestream in der Lauffunktion eines Threads zu erhalten. Das Problem mit der Blockierung (für getInputStream und getOutputStream) des Sockets liegt darin, dass es sich in der run-Funktion befindet ... die Lösung scheint darin zu bestehen, es in den Konstruktor zu stellen, die Variable zu speichern und dann auf die Variable im run zu verweisen. – Zimm3r

0

Ein OutputStream blockiert. Es hat wahrscheinlich eine Pufferung, aber das hilft nicht viel, wenn der Server niemals Bytes verbraucht (irgendein fester Puffer füllt sich irgendwann). Dein Freund hat also Recht, du musst in einem separaten Thread schreiben oder etwas fortgeschritteneres wie nio verwenden.

Auf der Leseseite können Sie available() verwenden, um Blockierungen zu vermeiden. Auf der Schreibseite existiert kein passender Aufruf. Ich wünschte, es wäre.