Domanda

Sto usando Java NIO per le mie connessioni socket, e il mio protocollo è basato su testo, quindi ho bisogno di essere in grado di convertire le stringhe ad ByteBuffers prima di scrivere al SocketChannel, e convertire i ByteBuffers in arrivo di nuovo a stringhe. Attualmente, sto usando questo codice:

public static Charset charset = Charset.forName("UTF-8");
public static CharsetEncoder encoder = charset.newEncoder();
public static CharsetDecoder decoder = charset.newDecoder();

public static ByteBuffer str_to_bb(String msg){
  try{
    return encoder.encode(CharBuffer.wrap(msg));
  }catch(Exception e){e.printStackTrace();}
  return null;
}

public static String bb_to_str(ByteBuffer buffer){
  String data = "";
  try{
    int old_position = buffer.position();
    data = decoder.decode(buffer).toString();
    // reset buffer's position to its original so it is not altered:
    buffer.position(old_position);  
  }catch (Exception e){
    e.printStackTrace();
    return "";
  }
  return data;
}

Questo funziona la maggior parte del tempo, ma mi chiedo se questo è il preferito (o semplice) modo di fare ogni direzione di questa conversione, o se c'è un altro modo per provare. Di tanto in tanto, e apparentemente a caso, chiama a encode() e decode() lancerà una eccezione java.lang.IllegalStateException: Current state = FLUSHED, new state = CODING_END, o simili, anche se sto usando un nuovo ByteBuffer oggetto ogni volta che una conversione è fatta. Ho bisogno di sincronizzare questi metodi? Un modo migliore per la conversione tra stringhe e ByteBuffers? Grazie!

È stato utile?

Soluzione

Controlla la CharsetEncoder e CharsetDecoder descrizioni API - Si dovrebbe seguire un specifica sequenza di chiamate di metodo per evitare questo problema. Ad esempio, per CharsetEncoder:

  1. Ripristina l'encoder tramite il metodo reset, a meno che non sia stato utilizzato in precedenza;
  2. Richiamare il metodo encode zero o più volte, fino a quando ingresso supplementare può essere disponibile, passando false per l'argomento endOfInput e riempimento del buffer di ingresso e lavando il buffer di uscita tra le chiamate;
  3. Richiamare il metodo encode un'ultima volta, passando true per l'argomento endOfInput; e poi
  4. Richiamare il metodo flush modo che l'encoder può lavare qualsiasi stato interno al buffer di uscita.

A proposito, questo è lo stesso approccio che sto usando per NIO anche se alcuni dei miei colleghi stanno convertendo ogni char direttamente a un byte con la consapevolezza che sono solo utilizzando ASCII, che posso immaginare è probabilmente più veloce.

Altri suggerimenti

A meno che le cose sono cambiate, si sta meglio con

public static ByteBuffer str_to_bb(String msg, Charset charset){
    return ByteBuffer.wrap(msg.getBytes(charset));
}

public static String bb_to_str(ByteBuffer buffer, Charset charset){
    byte[] bytes;
    if(buffer.hasArray()) {
        bytes = buffer.array();
    } else {
        bytes = new byte[buffer.remaining()];
        buffer.get(bytes);
    }
    return new String(bytes, charset);
}

Di solito buffer.hasArray () sarà o sempre vero o falso sempre a seconda del caso d'uso. In pratica, a meno che non si vuole veramente farlo funzionare in qualsiasi circostanza, è sicuro per ottimizzare via il ramo che non è necessario.

risposta da Adamski è buona e descrive i passi in un'operazione di codifica utilizzando il metodo encode generale (che prende un buffer di byte come uno degli ingressi)

Tuttavia, il metodo in questione (in questa discussione) è una variante di codifica - encode (CharBuffer a) . Questo è un metodo comodo che implementa l'intera operazione di codifica . (Si prega di vedere documenti java riferimento nel P.S.)

Come per la documentazione, Questo metodo non deve pertanto essere invocata se un'operazione di codifica è già in corso (che è ciò che accade nel codice di ZenBlender - utilizzando statica codificatore / decodificatore in un multi filettato ambiente).

Personalmente, mi piace usare convenienza metodi (oltre i metodi più generali di codifica / decodifica) che prendono via il carico eseguendo tutti i passaggi sotto le coperte.

ZenBlender e Adamski hanno già suggerito diverse opzioni di modi per farlo in modo sicuro questo nei loro commenti. elencandoli tutti qui:

  • Creare un nuovo oggetto codificatore / decodificatore quando necessario per ciascuna operazione (non efficiente come potrebbe portare a un gran numero di oggetti). O,
  • Utilizza un ThreadLocal per evitare di creare nuovo codificatore / decodificatore per ciascuna operazione. O,
  • Sincronizzare l'intera operazione di codifica / decodifica (questo potrebbe non essere preferito a meno che sacrificare un po 'di concorrenza è ok per il vostro programma)

P.S.

Documenti java riferimenti:

  1. Encode (convenienza) Metodo: http://docs.oracle.com/javase/6/docs/api/java/nio/charset/CharsetEncoder.html#encode%28java.nio.CharBuffer%29
  2. metodo di codifica generale: http://docs.oracle.com/javase/6/docs/api/java/nio/charset/CharsetEncoder.html#encode%28java.nio.CharBuffer,%20java. nio.ByteBuffer,% 20boolean% 29
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top