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

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

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


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


翻来覆去查问题,后来仔细查看报文内容,才发现报文中有8个汉字。这才想到,中文字节长度不能使用java.lang.String的length()方法获取。应该使用的是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)。


厚颜的贴上这个项目地址,欢迎star、fork和吐槽:


项目名称:cynomys

项目地址:https://github.com/howardliu-cn/cynomys

目录
相关文章
2 字节的 UTF-8 序列的字节 2 无效 解决方法
2 字节的 UTF-8 序列的字节 2 无效 解决方法: 用记事本打开xml文件,另存为 编码 选择 UTF-8,保存替换掉之前的文件,解决问题博客内容仅代表个人观点,如发现阐述有误,麻烦指正,谢谢!
4173 0
|
5月前
|
自然语言处理
字符编码问题之UTF-16不直接采用4字节定长编码来支持所有字符如何解决
字符编码问题之UTF-16不直接采用4字节定长编码来支持所有字符如何解决
52 0
|
5月前
字符编码问题之GB2312编码扩充到包括更多汉字和生僻字的如何解决
字符编码问题之GB2312编码扩充到包括更多汉字和生僻字的如何解决
99 0
单字节,双字节,四字节能够表示的数值大小范围分别是多少
单字节,双字节,四字节能够表示的数值大小范围分别是多少
|
C语言
文件的长度
文件的长度
259 0
文件的长度
|
存储 数据处理
位,字节与字
位、字节、字(bits, Bytes, words)是计算机数据存储的单位。位是最小的存储单位,每一个位存储一个1位的二进制码(0 or 1),一个字节由8位(8个二进制0 or 1 串)组成。而字通常为16、32或64个位组成。
821 0
|
C++
c++ 数据字节
#include using namespace std; void main() { cout
853 0
一个汉字占多少字节?
原文:一个汉字占多少字节? GBK编码,一个汉字占两个字节。 UTF-16编码,通常汉字占两个字节,CJKV扩展B区、扩展C区、扩展D区中的汉字占四个字节(一般字符的Unicode范围是U+0000至U+FFFF,而这些扩展部分的范围大于U+20000,因而要用两个UTF-16)。
2470 0