Pregunta

Estoy usando Java NIO por mis conexiones de socket, y mi protocolo está basado en texto, así que tengo que ser capaz de convertir cadenas en ByteBuffers antes de escribirlos en la SocketChannel, y convertir los entrantes ByteBuffers fondo de cuerdas. Actualmente, estoy usando este código:

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

Esto funciona la mayor parte del tiempo, pero me pregunto si esta es la forma preferida (o más simple) para hacer cada sentido de esta conversión, o si hay otra manera de probar. De vez en cuando, y aparentemente al azar, llama a encode() y decode() arrojará una excepción java.lang.IllegalStateException: Current state = FLUSHED, new state = CODING_END, o similares, incluso si estoy usando un nuevo ByteBuffer objeto cada vez que una conversión se realiza. Qué necesito para sincronizar estos métodos? Cualquier forma mejor de las conversiones entre cadenas y ByteBuffers? Gracias!

¿Fue útil?

Solución

Consulte el CharsetEncoder y CharsetDecoder descripciones de las API - que debe sigue una secuencia específica de método llama para evitar este problema. Por ejemplo, para CharsetEncoder:

  1. Reiniciar el codificador a través del método reset, a menos que no se ha utilizado antes;
  2. Invoque el método encode cero o más veces, siempre que la entrada adicional puede estar disponible, que pasa false para el argumento endOfInput y llenando la memoria intermedia de entrada y el lavado de la memoria intermedia de salida entre invocaciones;
  3. Invoque el método encode una última vez, pasando true para el argumento endOfInput; y después
  4. Invoque el método flush de modo que el codificador puede eliminar cualquier estado interno de la memoria intermedia de salida.

Por cierto, este es el mismo enfoque que estoy usando para NIO aunque algunos de mis colegas están convirtiendo cada Char directamente a un byte en el conocimiento que sólo se están utilizando ASCII, que puedo imaginar es probablemente más rápido.

Otros consejos

A menos que las cosas han cambiado, es mejor 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);
}

Por lo general buffer.hasArray () siempre será o bien verdadera o falsa siempre dependiendo de su caso de uso. En la práctica, a menos que realmente quiere que funcione bajo ninguna circunstancia, que es seguro para optimizar la rama de distancia que no es necesario.

Respuesta de Adamski es una buena y describe los pasos en una operación de codificación cuando se utiliza el método de codificación general (que tiene una memoria intermedia de bytes como una de las entradas)

Sin embargo, el método en cuestión (en esta discusión) es una variante de codificar - encode (CharBuffer en) . Este es un método de conveniencia que implementa toda la operación de codificación . (Por favor, ver datos de documentos de Java en P.S.)

De acuerdo con la documentación, Este método debería por lo tanto no puede invocarse si una operación de codificación ya está en progreso (que es lo que está sucediendo en el código de ZenBlender - usando estática codificador / decodificador en un multi roscado medio ambiente).

En lo personal, me gusta usar conveniencia métodos (a través de los métodos más generales envío / recepción), ya que quitan la carga mediante la realización de todos los pasos bajo las sábanas.

ZenBlender y Adamski ya se han sugerido varias opciones de maneras de hacer esto de manera segura en sus comentarios. Enumerarlos todos aquí:

  • Crea un nuevo objeto codificador / decodificador cuando sea necesario para cada operación (no es eficiente, ya que podría dar lugar a un gran número de objetos). O,
  • Utilice un ThreadLocal para evitar la creación de nuevo codificador / decodificador para cada operación. O,
  • Sincronizar toda la operación de codificación / decodificación (esto no podría ser preferible a no ser que sacrificar algo de concurrencia está bien para su programa)

P.S.

docs java referencias:

  1. Encode (conveniencia) Método: http://docs.oracle.com/javase/6/docs/api/java/nio/charset/CharsetEncoder.html#encode%28java.nio.CharBuffer%29
  2. método encode general: http://docs.oracle.com/javase/6/docs/api/java/nio/charset/CharsetEncoder.html#encode%28java.nio.CharBuffer,%20java. nio.ByteBuffer,% 20boolean% 29
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top