中文字節長度引起的數據丟失

最近在寫一個應用監控的項目,使用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)

厚顏的貼上這個項目地址,歡迎star、fork和吐槽:


個人主頁: http://www.howardliu.cn

個人博文: 中文字節長度引起的數據丟失

CSDN主頁: http://blog.csdn.net/liuxinghao

CSDN博文: 中文字節長度引起的數據丟失

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章