Java NIO:L'invio di messaggi di grandi dimensioni porta rapidamente alla troncato di pacchetti e di perdita di dati

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

Domanda

Ho questo brutto problema in cui l'invio di più, di messaggi di grandi dimensioni in rapida successione da una Java (NIO) server (Linux) per un cliente comporterà troncato i pacchetti.I messaggi devono essere di grandi dimensioni e spedito molto rapidamente per il problema.Qui è fondamentalmente ciò che il mio codice sta facendo (non codice vero e proprio, ma più o meno quello che succede):

//-- setup stuff: --
Charset charset = Charset.forName("UTF-8");
CharsetEncoder encoder = charset.newEncoder();
String msg = "A very long message (let's say 20KB)...";

//-- inside loop to handle incoming connections: --
ServerSocketChannel ssc = (ServerSocketChannel)key.channel();
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
sc.socket().setTcpNoDelay(true);
sc.socket().setSendBufferSize(1024*1024);

//-- later, actual sending of messages: --
for (int n=0; n<20; n++){
  ByteBuffer bb = encoder.encode(CharBuffer.wrap(msg+'\0'));
  sc.write(bb);
  bb.rewind();
}

Così, se i pacchetti sono abbastanza a lungo e inviato il più velocemente possibile (es.in un ciclo come questo senza ritardo), poi sull'altra estremità che spesso viene fuori qualcosa di simile a questo:

[COMPLETE PACKET 1]
[COMPLETE PACKET 2]
[COMPLETE PACKET 3]
[START OF PACKET 4][SOME OR ALL OF PACKET 5]

Non c'è perdita di dati, e i pacchetti di iniziare a correre insieme, in modo tale che l'inizio del pacchetto 5 (in questo esempio) arriva lo stesso messaggio l'inizio del pacchetto 4.Non è solo il troncamento, la sua esecuzione i messaggi insieme.

Immagino che questo è legato al buffer TCP o "dimensione della finestra", o che il server qui è solo offrire dati più velocemente di quanto il sistema operativo, o una scheda di rete, o qualcosa del genere, in grado di gestire.Ma come faccio a controllare e impedire che questo accada?Se posso ridurre la lunghezza del messaggio per l'utilizzo della sc.write(), ma poi di aumentare le ripetizioni, ho ancora eseguito nello stesso problema.Sembra di essere semplicemente un problema con la quantità di dati in un breve lasso di tempo.Non vedo che sc.write() sta gettando le eventuali eccezioni o (so che nel mio esempio di cui sopra, non sto controllando, ma nel mio test).

Sarei felice se potessi controllo a livello di codice, se non è pronto per ulteriori dati di sicurezza, e di mettere in ritardo, e attendere fino a quando si è pronti.Io non sono anche sicuro di se "sc.socket().setSendBufferSize(1024*1024);" non ha alcun effetto, o se avessi bisogno di modificare questo su Linux lato delle cose.C'è un modo per davvero "a filo" di un SocketChannel?Come lame soluzione, potrei provare a forzare esplicitamente completo di tutto ciò che è memorizzato nel buffer qualsiasi momento, sto cercando di inviare un messaggio di più di 10KB, per esempio (che non è molto spesso nella mia applicazione).Ma io non sono a conoscenza di un modo per forzare l'invio del buffer (o attendere fino a quando non ha inviato).Grazie per qualsiasi aiuto!

È stato utile?

Soluzione

Ci sono molte ragioni per cui sc.write() non inviare alcuni o di tutti i dati.Devi controllare il valore di ritorno e/o il numero di byte presenti nel buffer.

for (int n=0; n<20; n++){
  ByteBuffer bb = encoder.encode(CharBuffer.wrap(msg+'\0'));
  if(sc.write(bb) > 0 && bb.remaining() == 0) {
    // all data sent
  } else {
    // could not send all data.
  }
  bb.rewind();
}

Altri suggerimenti

Non si è controllare il valore di ritorno:

sc.write(bb);

Restituisce il numero di byte scritti, che potrebbe essere meno di dati disponibili nel buffer.A causa di come nio funziona probabilmente si può solo chiamare rimanenti() sul tuo bytebuffer per vedere se ci sono a sinistra.

Non ho fatto NIO di programmazione, ma secondo il Javadoc, sc.write() non scrivere tutto ByteBuffer se il SocketChannel è in modalità non bloccante (come la tua) e la presa di uscita del buffer è pieno.

Perché si scrive così rapidamente, è molto probabile che ti stanno inondando la vostra connessione di rete o il ricevitore non può tenere il passo.

Sarei felice se potessi controllo a livello di codice, se non è pronto per ulteriori dati di sicurezza

È necessario controllare il valore di ritorno di sc.write() per sapere se il vostro output buffer è pieno.

Non dare per scontato di avere alcun controllo sui dati che finisce in che pacchetto.

Di più qui: Qual è il modo migliore per monitorare una presa per i nuovi dati e quindi di elaborare i dati?

Si utilizza la modalità non bloccante:sc.configureBlocking(false);

Impostare il blocco per vero e il tuo codice dovrebbe funzionare così com'è.Suggerimenti fatti da altri qui per inviare conte e loop anche di lavoro.

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