解码(ByteBuffer): CharsetDecoder.decode() 与 Charset.decode() 的不同

今天测试的时候发现一个问题:

ByteBuffer inputBuffer = ByteBuffer.allocate(1024);

StringBuilder inputData = new StringBuilder(256);

int size = sChannel.read(inputBuffer);

Charset charset = Charset.forName("utf-8");
CharsetDecoder decoder = charset.newDecoder();

try
   {

//如果用utf-8解码,抛java.nio.charset.MalformedInputException:Malformed input length is 1

//如果用gbk解码,抛 java.nio.charset.MalformedInputException:Malformed input length is 2

//抛异常的几率在5%左右,也就是说并不是每次都会抛异常
    inputData.append(decoder.decode(inputBuffer)); 

  }
   catch (CharacterCodingException err)
   {
    System.err.println("CharacterCodingException: " + new String(inputBuffer.array()));
    err.printStackTrace();
   }

到百度搜索了一下,找到一篇:http://topic.csdn.net/u/20100310/17/4cf1c1a5-b01c-4774-a803-81ee245ae0eb.html

主要内容:

1. 这种现象是很正常的,由于网络上是一bit为单位传输,而TCP层上送的数据是以字节为单位,虽然NIO是以块为单位操作的,但是应用层处理时还是得以字节为单位处理。但是你使用的应该是ByteBuffer的派生类,(猜测是CharBuffer)它是以两个字节为单位处理报文的,因此很容易出现楼主的问题。

解决方案:
在处理数据之前,先判断ByteBuffer中的字节数,如果为奇数,则直接return false;不进行处理,等下一个消息来到时统一处理(注:可以这样做的原因是,这种现象是由于传输层上送的数据非完整应用层包) 2. 因为在GBK中字母占1byte汉字占2byte。ByteBuffer如果设置长度为偶数时,如果有奇数个字母那么最后的byte就是汉字的一部分,转码的时候就会出错。Bytebuffer设为奇数正好相反。可以判断ByteBuffer.get(index)大于0小于127的个数,然后确定最后一位是否要放到下次读入的第一位 3. 帖子的答案:“你是怎么处理数据包的,有处理粘包吗” ,粘包为何物? 再搜索,又找到一篇:http://www.blogjava.net/easywu/archive/2009/06/04/280100.html
如果直接使用Charset.decode()方法,则不会出现这样的错误: private final static Charset charset = Charset.forName("UTF-8");
inputData.append(charset.decode(inputBuffer).array()); //解码 改成这样后,再也不抛什么异常了,使用charset.decode()后,连异常都不需要捕获了。 经过测试,CharsetDecoder.decode() 与 Charset.decode() 的性能一样,通过查看API,charset类的所有方法都是线程安全的。     

2012-06-10

名品推荐:兰可欣 亚缇克兰 郁美净

原文地址:https://www.cnblogs.com/personnel/p/4583259.html