写点什么

中文字节长度引起的数据丢失

用户头像
看山
关注
发布于: 刚刚
中文字节长度引起的数据丢失

你好,我是看山。


最近在写一个应用监控的项目,使用 netty 作为数据传输。因为刚开始写,没有使用 Protobuf 之类的作为编码工具,只是使用的是 netty 自带的LengthFieldBasedFrameDecoder作为报文解析工具,自定义编码解码类,实现数据传输。


本来一切正常,结果在昨天测试的过程中,传输的数据体总是少 16 个字符,甚是奇怪。


翻来覆去查问题,后来仔细查看报文内容,才发现报文中有 8 个汉字。这才想到,中文字节长度不能使用java.lang.Stringlength()方法获取。应该使用的是getBytes()方法转成字节数组,在通过数组的length属性获取长度。


比如:


  • "abcd".length()的结果是:4

  • "abcd".getBytes().length的结果是:4

  • "中国威武".length()的结果是:4

  • "中国威武".getBytes().length的结果是:12

  • "中国v5".length()的结果是:4

  • "中国v5".getBytes().length的结果是:8


例子简单,但也能说明问题。在这里每个中文字节长度是 3,英文字母、数字、英文标点是 1。


所以在我的测试代码中,存在的 8 个汉字使用length()方法获取的长度是 8,比getBytes()方法的字节数组长度少了 16,所以在传输过程中总是少了 16 个字符(英文字符长度是 1)。


总的来说,在对中文进行转换字节的时候一定要注意,千万不要想当然的使用length()方法。还是要根据具体情况多试试。特立文标记此错误


下面附上代码:


编码器:MessageEncoder


import cn.howardliu.monitor.cynomys.net.struct.Message;import io.netty.buffer.ByteBuf;import io.netty.channel.ChannelHandlerContext;import io.netty.handler.codec.MessageToByteEncoder;import io.netty.util.CharsetUtil;
public class MessageEncoder extends MessageToByteEncoder<Message> { @Override protected void encode(ChannelHandlerContext ctx, Message msg, ByteBuf out) throws Exception { if (msg == null || msg.getHeader() == null) { throw new IllegalArgumentException("the encode message is null."); } out.writeInt(msg.getHeader().getCrcCode()); out.writeInt(msg.getHeader().getLength()); out.writeInt(msg.getHeader().getOpaque());
out.writeInt(msg.getHeader().getTag().length()); out.writeCharSequence(msg.getHeader().getTag(), CharsetUtil.UTF_8);
out.writeInt(msg.getHeader().getSysName().length()); out.writeCharSequence(msg.getHeader().getSysName(), CharsetUtil.UTF_8);
out.writeInt(msg.getHeader().getSysCode().length()); out.writeCharSequence(msg.getHeader().getSysCode(), CharsetUtil.UTF_8);
out.writeByte(msg.getHeader().getType()); out.writeByte(msg.getHeader().getCode()); out.writeByte(msg.getHeader().getFlagPath());
if (msg.getBody() == null) { out.writeInt(0); } else { out.writeInt(msg.getBody().getBytes().length); out.writeCharSequence(msg.getBody(), CharsetUtil.UTF_8); } out.setInt(4, out.readableBytes() - 8); }}
复制代码


解码器:MessageDecoder


import cn.howardliu.monitor.cynomys.net.struct.Header;import cn.howardliu.monitor.cynomys.net.struct.Message;import io.netty.buffer.ByteBuf;import io.netty.channel.ChannelHandlerContext;import io.netty.handler.codec.LengthFieldBasedFrameDecoder;import io.netty.util.CharsetUtil;
public class MessageDecoder extends LengthFieldBasedFrameDecoder { public MessageDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength) { super(maxFrameLength, lengthFieldOffset, lengthFieldLength); }
@Override protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { ByteBuf frame = (ByteBuf) super.decode(ctx, in); if (frame == null) { return null; } Message message = new Message() .setHeader( new Header() .setCrcCode(frame.readInt()) .setLength(frame.readInt()) .setOpaque(frame.readInt()) .setTag(frame.readCharSequence(frame.readInt(), CharsetUtil.UTF_8).toString()) .setSysName(frame.readCharSequence(frame.readInt(), CharsetUtil.UTF_8).toString()) .setSysCode(frame.readCharSequence(frame.readInt(), CharsetUtil.UTF_8).toString()) .setType(frame.readByte()) .setCode(frame.readByte()) .setFlagPath(frame.readByte()) ); if (frame.readableBytes() > 4) { message.setBody(frame.readCharSequence(frame.readInt(), CharsetUtil.UTF_8).toString()); } return message; }}
复制代码


解码器使用方式是new MessageDecoder(1024 * 1024 * 100, 4, 4)


厚颜的贴上这个项目地址:



你好,我是看山。游于码界,戏享人生。如果文章对您有帮助,请点赞、收藏、关注。


👇🏻欢迎关注我的公众号「看山的小屋」,领取精选资料👇🏻


发布于: 刚刚阅读数: 2
用户头像

看山

关注

公众号「看山的小屋」 2017.10.26 加入

游于码界,戏享人生。 未来不迎,当时不杂,既过不恋。

评论

发布
暂无评论
中文字节长度引起的数据丢失