Java:将string转换为ByteBuffer以及相关的问题

我为我的套接字连接使用Java NIO,并且我的协议是基于文本的,所以我需要能够将string转换为ByteBuffers,然后将它们写入到SocketChannel中,然后将传入的ByteBuffers转换回string。 目前,我正在使用这个代码:

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_ENDexception,或类似的,即使我每次使用一个新的ByteBuffer对象转换完成。 我需要同步这些方法吗? 任何更好的方法来转换string和ByteBuffers? 谢谢!

查看CharsetEncoderCharsetDecoder API描述 – 您应该按照特定的方法调用顺序来避免这个问题。 例如,对于CharsetEncoder

  1. 除非之前没有使用,否则通过reset方法重置编码器;
  2. 只要额外的input可用,调用encode方法零次或多次,为endOfInputparameter passingfalse并填充input缓冲区并在调用之间清空输出缓冲区;
  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。 在实践中,除非您真的希望在任何情况下都能正常工作,否则优化您不需要的分支是安全的。

通过回答Adamski是一个很好的一个,并介绍了在使用通用编码方法时的编码操作中的步骤(将字节缓冲区作为其中一个input)

然而,所讨论的方法(在这个讨论中)是一种编码 – 编码(CharBuffer in)的变体。 这是一个实现整个编码操作简便方法 。 (请参阅PS中的java docs参考)

根据文档, 如果编码操作已经在进行中 (这是ZenBlender的代码中发生了什么 – 在multithreading环境中使用静态编码器/解码器), 则不应调用此方法

就个人而言,我喜欢使用便捷的方法(通过更一般的编码/解码方法),因为它们通过执行所有步骤来减轻负担。

ZenBlender和Adamski已经提出了多种方法来在他们的评论中安全地做到这一点。 在这里列出它们全部:

  • 每个操作需要时创build一个新的编码器/解码器对象(效率不高,因为它可能导致大量的对象)。 要么,
  • 使用ThreadLocal可避免为每个操作创build新的编码器/解码器。 要么,
  • 同步整个编码/解码操作(这可能不是首选,除非牺牲一些并发性适用于您的程序)

PS

java文档参考:

  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