Warum vervollständigt Socket schreibt immer für den vollen Betrag sogar auf nicht-blockierende Sockets?

StackOverflow https://stackoverflow.com/questions/158121

Frage

Mit der Sun Java VM 1.5 oder 1.6 auf Windows, schließe ich einen nicht-blockierenden Socket. Ich fülle dann eine ByteBuffer mit einer Nachricht zur Ausgabe, und versuchen, die Socket write().

ich die Schreib erwarte nur teilweise ausfüllen, wenn die Menge geschrieben werden soll, größer als die Menge an Speicherplatz in dem TCP-Ausgabepuffer des Sockets (das ist, was ich intuitiv erwarten, dann ist es auch ziemlich mein Verständnis der Dokumente ), aber das ist nicht, was passiert. Die write() immer scheint geschrieben Berichterstattung des vollen Betrages zurück, auch wenn es mehrere Megabyte (die SO_SNDBUF der Buchse ist 8 KB, viel, viel kleiner als meine Multi-Megabyte Ausgabenachricht).

Ein Problem hierbei ist, dass ich nicht den Code testen, der den Fall behandelt, wo die Ausgabe zum Teil geschrieben (ein Interesse Satz von WRITE an einen Wähler zu registrieren und eine select() tun zu warten, bis der Rest kann geschrieben werden), als dieser Fall scheint nie zu geschehen. Was bin ich nicht zu verstehen?

War es hilfreich?

Lösung

Ich schaffte es, eine Situation zu reproduzieren, die ähnlich wie bei Ihnen sein könnten. Ich denke, ironisch genug, Ihre Empfänger die Daten verbraucht schneller, als Sie es gerade schreiben.

import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class MyServer {
  public static void main(String[] args) throws Exception {
    final ServerSocket ss = new ServerSocket(12345);
    final Socket cs = ss.accept();
    System.out.println("Accepted connection");

    final InputStream in = cs.getInputStream();
    final byte[] tmp = new byte[64 * 1024];
    while (in.read(tmp) != -1);

    Thread.sleep(100000);
  }
}



import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class MyNioClient {
  public static void main(String[] args) throws Exception {
    final SocketChannel s = SocketChannel.open();
    s.configureBlocking(false);
    s.connect(new InetSocketAddress("localhost", 12345));
    s.finishConnect();

    final ByteBuffer buf = ByteBuffer.allocate(128 * 1024);
    for (int i = 0; i < 10; i++) {
      System.out.println("to write: " + buf.remaining() + ", written: " + s.write(buf));
      buf.position(0);
    }
    Thread.sleep(100000);
  }
}

Wenn Sie den oben genannten Server ausführen und dann den oben Client versuchen, 10 Stücke von 128 kB von Daten zu schreiben, werden Sie feststellen, dass jeder Schreibvorgang ohne blockieren den gesamten Puffer schreibt. Wenn Sie jedoch die oben Server ändern nichts von der Verbindung zu lesen, werden Sie, dass nur die erste Schreiboperation auf dem Client sehen schreiben 128 kB, während alle nachfolgenden schreibt 0 zurück.

Ausgabe, wenn der Server von der Verbindung liest:

to write: 131072, written:  131072
to write: 131072, written:  131072
to write: 131072, written:  131072
...

Ausgabe, wenn der Server das Lesen nicht aus der Verbindung:

to write: 131072, written:  131072
to write: 131072, written:  0
to write: 131072, written:  0
...  

Andere Tipps

Ich habe mit UDP in Java gearbeitet und habe einige wirklich „interessant“ und völlig ohne Papiere Verhalten in den Java-NIO Sachen im Allgemeinen gesehen. Der beste Weg, um zu bestimmen, was geschieht, ist an der Quelle zu suchen, die mit Java kommt.

Ich würde wetten, auch ziemlich hoch, dass Sie eine bessere Umsetzung von dem, was Sie suchen in jeder anderen JVM Implementierung, wie IBM, aber ich kann nicht garantieren, dass selbst bei ihnen ohne Blick finden könnten.

Ich kann es nicht überall dokumentiert finden, aber IIRC [1], send () wird garantiert, um entweder a) vollständig mit dem mitgelieferten Puffer zu senden, oder b) fehlschlagen. Es wird nie teilweise die Sende abzuschließen.

[1] ich mehr Winsock-Implementierungen geschrieben habe (für Win 3.0, Win 95, Win NT, usw.), so kann dieser Winsock spezifisch seinen (eher als generisches Sockets) Verhalten.

Ich werde einen großen Sprung des Glaubens machen und davon ausgehen, dass die zugrunde liegenden Netzwerk-Provider für Java das gleiche wie für C ... das O / S ordnet mehr als nur für jede Steckdose SO_SNDBUF. Ich wette, wenn Sie Ihre Sendecode in ein setzen für (1,100000) Schleife, Sie würde schließlich eine Schreib erhalten, die mit einem Wert kleiner als die angeforderte erfolgreich ist.

Sie sollten wirklich an einem NIO Rahmen aussehen MINA oder Grizzly . Ich habe MINA mit großem Erfolg in einem Unternehmen Chat-Server verwendet. Es ist auch Chat-Server in der Openfire verwendet. Grizzly in Suns Java EE-Implementierung verwendet.

Wo senden Sie die Daten? Beachten Sie, dass das Netzwerk als ein Puffer wirkt, der zumindest gleich groß wie Ihre SO_SNDBUF plus der SO_RCVBUF des Empfängers. Fügen Sie diese an die Leseaktivität durch den Empfänger, wie durch Alexander erwähnt, und Sie können eine Menge von Daten erhalten durchnässt.

scroll top