Java - Язык:Преобразование строки в ByteBuffer и из него и связанные с этим проблемы

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

Вопрос

Я использую Java NIO для своих подключений к сокетам, и мой протокол основан на тексте, поэтому мне нужно иметь возможность преобразовывать строки в байт-буферы перед их записью в SocketChannel и преобразовывать входящие байт-буферы обратно в строки.В настоящее время я использую этот код:

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

Это работает большую часть времени, но я сомневаюсь, является ли это предпочтительным (или самым простым) способом выполнить каждое направление этого преобразования, или есть другой способ попробовать.Время от времени, и, казалось бы, наугад, звонит encode() и decode() будет бросать java.lang.IllegalStateException: Current state = FLUSHED, new state = CODING_END исключение или что-то подобное, даже если я использую новый объект ByteBuffer каждый раз, когда выполняется преобразование.Нужно ли мне синхронизировать эти методы?Есть лучший способ конвертировать между Strings и ByteBuffers?Спасибо!

Это было полезно?

Решение

Ознакомьтесь с CharsetEncoder и CharsetDecoder Описания API - Вы должны следовать определенная последовательность вызовов метода чтобы избежать этой проблемы.Например, для CharsetEncoder:

  1. Сбросьте кодировщик с помощью reset метод, если только он не использовался ранее;
  2. Вызвать encode метод ноль или более раз, при условии, что могут быть доступны дополнительные входные данные, передавая false для аргумента endOfInput и заполнения входного буфера и очистки выходного буфера между вызовами;
  3. Вызвать encode метод в последний раз, проходя true для аргумента endOfInput;и тогда
  4. Вызвать flush метод, позволяющий кодеру сбрасывать любое внутреннее состояние в выходной буфер.

Кстати, это тот же подход, который я использую для NIO, хотя некоторые из моих коллег преобразуют каждый символ непосредственно в байт, зная, что они используют только ASCII, что, как я могу себе представить, вероятно, быстрее.

Другие советы

Если что-то не изменилось, вам лучше с

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

Обычно buffer.hasArray() будет либо всегда иметь значение true, либо всегда иметь значение false в зависимости от вашего варианта использования.На практике, если вы действительно не хотите, чтобы это работало ни при каких обстоятельствах, безопасно оптимизировать ненужную вам ветку.

Ответ Адамски является хорошим и описывает шаги в операции кодирования при использовании общего метода encode (который принимает байтовый буфер в качестве одного из входных данных)

Однако рассматриваемый метод (в этом обсуждении) является вариантом encode - кодирование (ввод буфера символов).Это такой удобный метод, который реализует всю операцию кодирования.(Пожалуйста, смотрите ссылку на java docs в P.S.)

Согласно документам, Поэтому этот метод не следует вызывать, если операция кодирования уже выполняется (именно это и происходит в коде ZenBlender - использование статического кодировщика / декодера в многопоточной среде).

Лично мне нравится использовать удобство методы (по сравнению с более общими методами кодирования / декодирования), поскольку они снимают с себя нагрузку, выполняя все шаги под прикрытием.

ЗенБлендер и Адамски уже предложили несколько способов безопасно сделать это в своих комментариях.Перечисляя их все здесь:

  • Создавайте новый объект кодера / декодера, когда это необходимо для каждой операции (неэффективно, поскольку это может привести к большому количеству объектов).или,
  • Используйте ThreadLocal, чтобы избежать создания нового кодера / декодера для каждой операции.или,
  • Синхронизируйте всю операцию кодирования / декодирования (это может быть нежелательно, если только ваша программа не согласится пожертвовать некоторым параллелизмом).

P.S.

ссылки на документы java docs:

  1. Метод кодирования (удобства): http://docs.oracle.com/javase/6/docs/api/java/nio/charset/CharsetEncoder.html#encode%28java.nio.CharBuffer%29
  2. Общий метод кодирования: http://docs.oracle.com/javase/6/docs/api/java/nio/charset/CharsetEncoder.html#encode%28java.nio.CharBuffer,%20java.nio.ByteBuffer,%20boolean%29
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top