龙空技术网

netty教程15-netty自定义协议与编解码器ByteToMessageCodec

南京彭于雁 177

前言:

当前你们对“netty 教程”大致比较关怀,兄弟们都需要了解一些“netty 教程”的相关内容。那么小编同时在网上汇集了一些对于“netty 教程””的相关知识,希望兄弟们能喜欢,小伙伴们快快来了解一下吧!

1 实际开发过程中,我们会自定义一个传输协议,说简单点就是发送方和接收方统一一个规则,比如发送的报文中包括以下字段:

1 魔鬼数字

2 版本号

3 序列化算法类型

4 指令类型

5 请求序列号

6 消息内容长度

7 消息内容

最好1,2,3,4,5,6的字节长度加起来是2的指数倍,这样看起来专业一点

比如我这里发送的是一个登陆的消息:

上代码:

package com.study.nio.ph2.e7;import lombok.AllArgsConstructor;import lombok.Data;/** * @program: isc-study * * @description: * * @author: wangjinwei * * @create: 2021-11-04 14:15 **/@Data@AllArgsConstructorpublic class LoginRequestMessage extends Message {    private String loginname;    private String password;    private String nickname;    @Override    public Byte getMessageType() {        return 1;    }    @Override    public int getSequenceId() {        return 0;    }}package com.study.nio.ph2.e7;import java.io.Serializable;public abstract class Message implements Serializable {    /**     * 消息类型     * @return     */    public abstract Byte getMessageType();    /**     * 字节序列     * @return     */    public abstract int getSequenceId();}package com.study.nio.ph2.e7;import io.netty.buffer.ByteBuf;import io.netty.channel.ChannelHandlerContext;import io.netty.handler.codec.ByteToMessageCodec;import lombok.extern.slf4j.Slf4j;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.util.List;/** * @program: isc-study * @description: 自定义协议 编解码 * @author: wangjinwei * @create: 2021-11-04 14:11 **/@Slf4jpublic class MessageCodec extends ByteToMessageCodec<Message> {    @Override    protected void encode(ChannelHandlerContext ctx, Message msg, ByteBuf out) throws Exception {        //1  4个字节魔数        out.writeBytes(new byte[]{1, 2, 3, 4});        //2  1个字节的版本        out.writeByte(2);        //3  1个字节的序列化方式 jdk 0 , json 1        out.writeByte(0);        //4  1个字节的指令类型        out.writeByte(msg.getMessageType());        //5  4个字节的序列        out.writeInt(msg.getSequenceId());        // 无意义,对齐填充 凑齐2的指数倍        out.writeByte(0xff);        //内容转换成字节数组        ByteArrayOutputStream bos = new ByteArrayOutputStream();        ObjectOutputStream oos = new ObjectOutputStream(bos);        oos.writeObject(msg);        byte[] bytes = bos.toByteArray();        // 6 4个字节写入长度        out.writeInt(bytes.length);        // 7 写入内容        out.writeBytes(bytes);    }    @Override    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {        int magicNum = in.readInt();        byte version = in.readByte();        byte seriType = in.readByte();        byte messageType = in.readByte();        int seq = in.readInt();        in.readByte();        int len = in.readInt();        byte[] bytes = new byte[len];        in.readBytes(bytes, 0, len);        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));        Message message = (Message) ois.readObject();        System.out.printf("magicNum=%s, version=%s, seriType=%s, messageType=%s, seq=%d, len=%d", magicNum, version, seriType, messageType, seq, len);        log.info("{}", message);        out.add(message);    }}@Slf4jpublic class TestMessage {    public static void main(String[] args) throws Exception {        EmbeddedChannel channel = new EmbeddedChannel(                new LoggingHandler(LogLevel.INFO),                new MessageCodec()        );        LoginRequestMessage message = new LoginRequestMessage("zhangsan", "123", "张三");        ByteBuf buf = ByteBufAllocator.DEFAULT.buffer();        new MessageCodec().encode(null,message,buf);        channel.writeInbound(buf);    }}

运行结果如下:

这里为了安全起见,加入一个LengthFieldBasedFrameDecoder,这样才不会发生拆包粘包

修改代码:

package com.study.nio.ph2.e7;import io.netty.buffer.ByteBuf;import io.netty.buffer.ByteBufAllocator;import io.netty.channel.embedded.EmbeddedChannel;import io.netty.handler.codec.LengthFieldBasedFrameDecoder;import io.netty.handler.logging.LogLevel;import io.netty.handler.logging.LoggingHandler;import lombok.extern.slf4j.Slf4j;/** * @program: isc-study * @description: * @author: wangjinwei * @create: 2021-11-04 16:06 **/@Slf4jpublic class TestMessage {    public static void main(String[] args) throws Exception {        EmbeddedChannel channel = new EmbeddedChannel(                new LoggingHandler(LogLevel.INFO),                new LengthFieldBasedFrameDecoder(1024,12,4,0,0),                new MessageCodec()        );        LoginRequestMessage message = new LoginRequestMessage("zhangsan", "123", "张三");        ByteBuf buf = ByteBufAllocator.DEFAULT.buffer();        new MessageCodec().encode(null,message,buf);//        channel.writeInbound(buf);        ByteBuf s1 = buf.slice(0, 100);        ByteBuf s2 = buf.slice(100, buf.readableBytes() - 100);        s1.retain(); // 引用计数 2        channel.writeInbound(s1); // release 1        channel.writeInbound(s2);    }}

如果不加入LengthFieldBasedFrameDecoder就报错了

标签: #netty 教程