Perché le scritture di SocketChannel sono sempre complete per l'intero importo anche su socket non bloccanti?

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

Domanda

Utilizzando Sun Java VM 1.5 o 1.6 su Windows, collego un socket non bloccante. Quindi riempio un ByteBuffer con un messaggio da emettere e provo a write () in SocketChannel.

Mi aspetto che la scrittura si completi solo parzialmente se la quantità da scrivere è maggiore della quantità di spazio nel buffer di output TCP del socket (questo è ciò che mi aspetto intuitivamente, è anche praticamente la mia comprensione di docs ), ma non è quello che succede. Il write () sempre sembra restituire riportando l'intero importo scritto, anche se si tratta di diversi megabyte (il SO_SNDBUF del socket è 8 KB, molto, molto meno del mio output multi-megabyte messaggio).

Un problema qui è che non riesco a testare il codice che gestisce il caso in cui l'output è parzialmente scritto (registrando un set di interessi di WRITE su un selettore e facendo un select ( ) per attendere che il resto possa essere scritto), poiché quel caso non sembra mai accadere. Cosa non capisco?

È stato utile?

Soluzione

Sono riuscito a riprodurre una situazione che potrebbe essere simile alla tua. Penso che, ironicamente, il tuo destinatario stia consumando i dati più velocemente di quanto tu li scriva.

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

Se esegui il server sopra e fai il tentativo del client sopra di scrivere 10 blocchi di 128 kB di dati, vedrai che ogni operazione di scrittura scrive l'intero buffer senza bloccarlo. Tuttavia, se si modifica il server sopra per non leggere nulla dalla connessione, si vedrà che solo la prima operazione di scrittura sul client scriverà 128 kB, mentre tutte le scritture successive restituiranno 0 .

Output quando il server sta leggendo dalla connessione:

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

Output quando il server non sta leggendo dalla connessione:

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

Altri suggerimenti

Ho lavorato con UDP in Java e ho visto alcuni "interessanti" davvero interessanti e comportamento completamente non documentato nella roba Java NIO in generale. Il modo migliore per determinare cosa sta succedendo è guardare alla fonte fornita con Java.

Scommetto anche piuttosto che potresti trovare una migliore implementazione di ciò che stai cercando in qualsiasi altra implementazione JVM, come quella di IBM, ma non posso garantirlo senza guardarli da solo.

Non riesco a trovarlo documentato da nessuna parte, ma IIRC [1], send () è garantito per a) inviare completamente il buffer fornito o b) fallire. Non completerà mai parzialmente l'invio.

[1] Ho scritto più implementazioni di Winsock (per Win 3.0, Win 95, Win NT, ecc.), quindi questo potrebbe essere un comportamento specifico di Winsock (anziché socket generico).

Farò un grande salto di fiducia e presumo che il provider di rete sottostante per Java sia lo stesso di C ... l'O / S alloca più di un semplice SO_SNDBUF per ogni socket. Scommetto che se metti il ??tuo codice di invio in un ciclo for (1.100000), alla fine otterrai una scrittura che ha successo con un valore inferiore a quello richiesto.

Dovresti veramente guardare un framework NIO come MINA o Grizzly . Ho usato MINA con grande successo in un server di chat aziendale. Viene anche utilizzato nel Openfire server di chat. Grizzly viene utilizzato nell'implementazione JavaEE di Sun.

Dove stai inviando i dati? Tieni presente che la rete funge da buffer di dimensioni almeno uguali a SO_SNDBUF più SO_RCVBUF del ricevitore. Aggiungi questo all'attività di lettura da parte del destinatario come menzionato da Alexander e puoi ottenere un sacco di dati assorbiti.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top