netty-自定義協(xié)議編解碼器

1. 協(xié)議格式

 * * * 自定義協(xié)議 數(shù)據(jù)包格式
 * * * -----------------------------------
 * * Length 數(shù)據(jù)包長度   Data長度 + Head長度
 * * Version    協(xié)議版本號   默認0x0
 * * Command    操作指令碼   十六進制
 * * MsgType    數(shù)據(jù)類型    0x0:Json,0x1:ProtoBuf,0x2:Xml,默認:0x0
 * * SecretKey  密鑰key   默認0x0,明文
 * * Data   業(yè)務(wù)數(shù)據(jù)包

2. 自定義編碼器

/**
 * @author wangxx
 * @data 2020/6/19
 * describe 自定義編碼器
 * * * -----------------------------------
 */
public class CustomizeEncoder extends MessageToByteEncoder<IMMessage> {
    private static final String TAG = "CustomizeEncoder";
    /**
     * 頭部固定長度7個字節(jié)
     */
    public static final int HEAD_LENGTH = 7;
    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, IMMessage message, ByteBuf byteBuf) throws Exception {
        Log.i(TAG, channelHandlerContext.toString() + ",pk=" + message.toString());
        if (null == message) {
            throw new Exception();
        }
        String body = message.getData();
        byte[] bodyBytes = null;
        //包長度
        int headLength = message.getLength();
        if (!TextUtils.isEmpty(body)) {
            bodyBytes = body.getBytes(Charset.forName("utf-8"));
            headLength = bodyBytes.length+HEAD_LENGTH;
        }

        byteBuf.writeShort(headLength);
        //版本號
        byteBuf.writeByte(message.getVersion());
        //操作指令
        byteBuf.writeShort(message.getCommand());
        //數(shù)據(jù)類型
        byteBuf.writeByte(message.getMsgType());
        //密鑰key
        byteBuf.writeByte(message.getSecretKey());
        //數(shù)據(jù)包
        if (bodyBytes != null && bodyBytes.length > 0) {
            byteBuf.writeBytes(bodyBytes);
        }

    }
}

3. 自定義解碼器

public class CustomizeDecoder extends ByteToMessageDecoder {

    private static final String TAG = "CustomizeDecoder";
    /**
     * 頭部固定長度7個字節(jié)
     */
    public static final int HEAD_LENGTH = 7;


    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf buffer, List<Object> out) throws Exception {
        Log.i(TAG, "decode ChannelHandlerContext=" + channelHandlerContext.toString() + " ,bytebuf=" + buffer.toString() + ",list=" + out.toString());
        Log.i(TAG, "decode ChannelHandlerContext=" + convertByteBufToString(buffer));
        if (buffer.readableBytes() < HEAD_LENGTH) {
            Log.i(TAG, "數(shù)據(jù)不足一條");
            return;
        }

        // 記錄包頭開始的index
        int beginReader;
        int contentLength;
        IMMessage message = new IMMessage();
        while (true) {
            // 獲取包頭開始的index
            beginReader = buffer.readerIndex();
            // 標記包頭開始的index
            buffer.markReaderIndex();
            // 讀到了協(xié)議的開始標志瞳脓,結(jié)束while循環(huán)
            short headLength = buffer.readShort();
            //版本號
            byte version = buffer.readByte();
            //操作指令
            int command = buffer.readUnsignedShort();
            //數(shù)據(jù)類型
            byte msgType = buffer.readByte();
            //密鑰key
            byte secretKey = buffer.readByte();
            message.setLength(headLength);
            message.setVersion(version);
            message.setCommand(command);
            message.setMsgType(msgType);
            message.setSecretKey(secretKey);
            //按照協(xié)議計算data的長度
            contentLength = headLength - HEAD_LENGTH;
            if (buffer.readableBytes() == contentLength) {
                break;
            }

            // 未讀到包頭亿卤,略過一個字節(jié)
            // 每次略過,一個字節(jié),去讀取中姜,包頭信息的開始標記
            buffer.resetReaderIndex();
            buffer.readByte();

            // 當略過睦柴,一個字節(jié)之后徙融,
            // 數(shù)據(jù)包的長度毁菱,又變得不滿足
            // 此時,應(yīng)該結(jié)束衷佃。等待后面的數(shù)據(jù)到達
            if (buffer.readableBytes() < HEAD_LENGTH) {
                return;
            }
        }

        // 消息的長度

        // 判斷請求數(shù)據(jù)包數(shù)據(jù)是否到齊
        if (buffer.readableBytes() < contentLength) {
            // 還原讀指針
            buffer.readerIndex(beginReader);
            return;
        }

        // 讀取data數(shù)據(jù)
        byte[] bytes = new byte[contentLength];
        buffer.readBytes(bytes);

        if (message.getCommand() == TcpAPI.MIMSocketCommandHeartBeatPong) {
            message.setTimeStamp(ByteUtil.bytesToInt(bytes));
        } else {
            message.setData(new String(bytes, StandardCharsets.UTF_8));
        }
        out.add(message);
    }


    public String convertByteBufToString(ByteBuf buf) {
        String str;
        if (buf.hasArray()) { // 處理堆緩沖區(qū)
            str = new String(buf.array(), buf.arrayOffset() + buf.readerIndex(), buf.readableBytes());
        } else { // 處理直接緩沖區(qū)以及復合緩沖區(qū)
            byte[] bytes = new byte[buf.readableBytes()];
            buf.getBytes(buf.readerIndex(), bytes);
            str = new String(bytes, 0, buf.readableBytes());
        }
        return str;
    }

}

4. 協(xié)議

/**
 * 包協(xié)議
 */
public class IMMessage {
    /**
     * 數(shù)據(jù)包長度
     */
    private int Length=0x0;
    /**
     * 版本
     */
    private byte Version = 0x0;
    /**
     * 命令
     */
    private int Command;
    /**
     *  0x0:Json,0x1:ProtoBuf,0x2:Xml,默認:0x0
     */
    private byte MsgType = 0x0;
    /**
     * 密鑰key | 默認0x0,明文
     */
    private byte SecretKey = 0x0;
    /**
     * 數(shù)據(jù)
     */
    private String Data;
    /**
     * 服務(wù)器時間(2分鐘收到服務(wù)端給的)
     */
    private int timeStamp;

    public int getLength() {
        return Length;
    }

    public void setLength(int length) {
        Length = length;
    }

    public byte getVersion() {
        return Version;
    }

    public void setVersion(byte version) {
        Version = version;
    }

    public int  getCommand() {
        return Command;
    }

    public void setCommand(int command) {
        Command = command;
    }

    public byte getMsgType() {
        return MsgType;
    }

    public void setMsgType(byte msgType) {
        MsgType = msgType;
    }

    public byte getSecretKey() {
        return SecretKey;
    }

    public void setSecretKey(byte secretKey) {
        SecretKey = secretKey;
    }

    public String getData() {
        return Data;
    }

    public void setData(String data) {
        Data = data;
    }

    public void setData(BaseSocketBody body){
        Gson gson = new Gson();
        this.Data = gson.toJson(body);
    }

    public int getTimeStamp() {
        return timeStamp;
    }

    public void setTimeStamp(int timeStamp) {
        this.timeStamp = timeStamp;
    }

    @Override
    public String toString() {
        return "IMMessage{" +
                "Length=" + Length +
                ", Version=" + Version +
                ", Command=" + Command +
                ", MsgType=" + MsgType +
                ", SecretKey=" + SecretKey +
                ", Data='" + Data + '\'' +
                ", timeStamp=" + timeStamp +
                '}';
    }
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末趟卸,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子氏义,更是在濱河造成了極大的恐慌锄列,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惯悠,死亡現(xiàn)場離奇詭異邻邮,居然都是意外死亡,警方通過查閱死者的電腦和手機克婶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門筒严,熙熙樓的掌柜王于貴愁眉苦臉地迎上來丹泉,“玉大人,你說我怎么就攤上這事鸭蛙∧『蓿” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵娶视,是天一觀的道長晒哄。 經(jīng)常有香客問我,道長肪获,這世上最難降的妖魔是什么寝凌? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮孝赫,結(jié)果婚禮上较木,老公的妹妹穿的比我還像新娘。我一直安慰自己寒锚,他們只是感情好,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布违孝。 她就那樣靜靜地躺著刹前,像睡著了一般。 火紅的嫁衣襯著肌膚如雪雌桑。 梳的紋絲不亂的頭發(fā)上喇喉,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機與錄音校坑,去河邊找鬼拣技。 笑死,一個胖子當著我的面吹牛耍目,可吹牛的內(nèi)容都是我干的膏斤。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼邪驮,長吁一口氣:“原來是場噩夢啊……” “哼莫辨!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起毅访,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤沮榜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后喻粹,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蟆融,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年守呜,在試婚紗的時候發(fā)現(xiàn)自己被綠了型酥。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片山憨。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖冕末,靈堂內(nèi)的尸體忽然破棺而出萍歉,到底是詐尸還是另有隱情,我是刑警寧澤档桃,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布枪孩,位于F島的核電站,受9級特大地震影響藻肄,放射性物質(zhì)發(fā)生泄漏蔑舞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一嘹屯、第九天 我趴在偏房一處隱蔽的房頂上張望攻询。 院中可真熱鬧,春花似錦州弟、人聲如沸钧栖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拯杠。三九已至,卻和暖如春啃奴,著一層夾襖步出監(jiān)牢的瞬間潭陪,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工最蕾, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留依溯,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓瘟则,卻偏偏與公主長得像黎炉,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子醋拧,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344