Java: преобразование строки в ByteBuffer и из него и связанные с этим проблемы
Я использую Java NIO для моих сокетных соединений, и мой протокол основан на тексте, поэтому мне нужно иметь возможность конвертировать строки в ByteBuffers перед записью их в SocketChannel и конвертировать входящие ByteBuffers обратно в строки. В настоящее время я использую этот код:
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() бросит a
java.lang.IllegalStateException: Current state = FLUSHED, new state = CODING_END исключение или подобное, даже если я использую новый объект ByteBuffer каждый раз, когда выполняется преобразование. Нужно ли синхронизировать эти методы? Какой-нибудь лучший способ для преобразования между строками и объектов ByteBuffer? Спасибо!
4 ответов:
Проверьте
CharsetEncoderиCharsetDecoderописания API - вы должны следовать определенная последовательность вызовов метода чтобы избежать этой проблемы. Например, дляCharsetEncoder:
- переустановите шифратор через
resetметод, если он не был использован ранее;- вызов
encodeметод ноль или более раз, пока дополнительный вход может быть доступен, передаваяfalseдля аргумента endOfInput и заполнение входного буфера и промывка выходного буфера между вызовами;- вызов
encodeметод в последний раз, проходяtrueдля аргумента endOfInput; а затем- вызов
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); }обычно буфер.hasArray() будет либо всегда true, либо всегда false, в зависимости от вашего варианта использования. На практике, если вы действительно не хотите, чтобы он работал при любых обстоятельствах, безопасно оптимизировать ветку, которая вам не нужна.
ответ Адамского является хорошим и описывает шаги в операции кодирования при использовании общего метода кодирования (который принимает байтовый буфер в качестве одного из входов)
однако рассматриваемый метод (в данном обсуждении) является вариантом кодирования - encode (CharBuffer in). Это же удобный метод, который реализует всю операцию кодирования. (См. Ссылка на Java Docs в С. П.)
согласно документации, этот метод поэтому не следует вызывать, если операция кодирования уже выполняется (что и происходит в коде ZenBlender-использование статического кодера/декодера в многопоточной среде).
лично мне нравится использовать удобство методы (более общие методы кодирования / декодирования), поскольку они снимают нагрузку, выполняя все шаги под обложками.
ZenBlender и Adamski уже предложили несколько вариантов способов смело делайте это в своих комментариях. Перечисляя их все здесь:
- создать новый объект кодера / декодера, когда это необходимо для каждой операции (не эффективно, поскольку это может привести к большому количеству объектов). Или,
- использовать ThreadLocal, чтобы избежать создания нового кодера/декодера для каждой операции. Или,
- синхронизировать всю операцию кодирования / декодирования (это может быть не предпочтительнее, если пожертвование некоторым параллелизмом не подходит для вашей программы)
П. С.
ссылки на документы java:
- кодирование (удобство) метод: http://docs.oracle.com/javase/6/docs/api/java/nio/charset/CharsetEncoder.html#encode%28java.nio.CharBuffer%29
- общий метод кодирования: http://docs.oracle.com/javase/6/docs/api/java/nio/charset/CharsetEncoder.html#encode%28java.nio.CharBuffer,%20java.nio.ByteBuffer,%20boolean%29
I am getting the same exception "java.lang.Illegalstate.exception when i run the below code protected static SAMStringResult execScript(ConfParams params, String routineName, String[] enquiryInParams) { SAMStringResult res = new SAMStringResult(); res.setSuccess(false, null); res.message = "T24Tools.ExecScript: failed for unknown reason"; res.setResult(""); try { DefaultJConnectionFactory jcf = new DefaultJConnectionFactory(); jcf.setHost(params.Host); jcf.setPort(params.Port); JConnection jc = jcf.getConnection(params.User, params.Password); JSubroutineParameters rtnParams = new JSubroutineParameters(); JDynArray shellNameParam = new JDynArray(routineName); rtnParams.add(shellNameParam); JDynArray outputParam = new JDynArray(routineName); rtnParams.add(outputParam); JSubroutineParameters rtnOutParams = jc.call("SAM.EXEC.SHELL", rtnParams); res.setSuccess(true, null); res.message = "T24Tools.ExecScript: job executed"; JDynArray resParam = rtnOutParams.get(1); String resStr = ""; System.out.println("Parameterssize :" + resParam.getNumberOfAttributes()); if (resParam != null) { for (int i = 0; i < 90145; i++) { if (i > 0) { resStr += "\n"; } resStr += resParam.get(i + 1); } } String fixedResStr = removeInvalidXmlCharacters(resStr); res.setResult(fixedResStr); jc.close(); } catch (Exception ex) { System.out.println("Exception in execScript :"+ex.getMessage()); res.setSuccess(false, ex); res.message = "T24Tools.ExecScript: unexpected error ocured"; res.setResult(""); } return res; } When i try to loop the for loop in my code 10,000 times. Code is working fine. But the actual number of times it has to run is 90,145 times. In between the iteration this exception araises. I am unable to find the reason behind this exception. can anyone help me in this?
Comments