Netty自定义协议

简介: 先写一个Messsage类,解码的时候将要把ByteBuf解码为Message

首发于Enaium的个人博客


先写一个Messsage类,解码的时候将要把ByteBuf解码为Message

public class Message implements Serializable {
   
    private final String data;

    public Message(String data) {
   
        this.data = data;
    }

    @Override
    public String toString() {
   
        return "Message{" +
                "data='" + data + '\'' +
                '}';
    }
}

之后创建MessageCodec类,继承MessageToMessageCodec

入站为ByteBuf解码为Message,出站为Message编码为ByteBuf

重写编码的解码方法

public class MessageCodec extends MessageToMessageCodec<ByteBuf, Message> {
   
    @Override
    public void encode(ChannelHandlerContext channelHandlerContext, Message message, List<Object> list) throws Exception {
   
    }

    @Override
    public void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
   
    }
}

这里用四个字节表示内容的长度,最后是所有内容,这里使用JDK来序列化

public void encode(ChannelHandlerContext channelHandlerContext, Message message, List<Object> list) throws Exception {
   
    ByteBuf buffer = channelHandlerContext.alloc().buffer();
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
    objectOutputStream.writeObject(message);
    byte[] bytes = byteArrayOutputStream.toByteArray();
    buffer.writeInt(bytes.length);
    buffer.writeBytes(bytes);
    list.add(buffer);
}

怎么编码的就怎么解码,读取长度读取内容

public void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
   
    int length = byteBuf.readInt();
    byte[] bytes = new byte[length];
    byteBuf.readBytes(bytes);
    ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(bytes));
    Message message = (Message) objectInputStream.readObject();
    System.out.println("message = " + message);
    list.add(message);
}

一个简单的协议就写好了,现在就来测试一下

使用EmbeddedChannel这个Channel来进行测试,处理器就添加一个日志处理器和刚才写好的编解码器

写入Message

EmbeddedChannel embeddedChannel = new EmbeddedChannel(new LoggingHandler(LogLevel.INFO), new MessageCodec());
embeddedChannel.writeOutbound(new Message("HELLO WORLD"));

运行一下试试

写入了92个字节的数据,长度为0x58转为10进制为88加上长度占的4个字节刚好92个字节

信息: [id: 0xembedded, L:embedded - R:embedded] WRITE: 92B
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 00 00 00 58 ac ed 00 05 73 72 00 19 63 6e 2e 65 |...X....sr..cn.e|
|00000010| 6e 61 69 75 6d 2e 6d 65 73 73 61 67 65 2e 4d 65 |naium.message.Me|
|00000020| 73 73 61 67 65 95 ee d0 49 e2 3a be 6d 02 00 01 |ssage...I.:.m...|
|00000030| 4c 00 04 64 61 74 61 74 00 12 4c 6a 61 76 61 2f |L..datat..Ljava/|
|00000040| 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 78 70 74 00 |lang/String;xpt.|
|00000050| 0b 48 45 4c 4c 4f 20 57 4f 52 4c 44             |.HELLO WORLD    |
+--------+-------------------------------------------------+----------------+

接着测试解码,这里粗暴的使用了刚才的日志

embeddedChannel.writeInbound(Unpooled.wrappedBuffer(new byte[]{
   
        0x00, 0x00, 0x00, 0x58, (byte) 0xac, (byte) 0xed, 0x00, 0x05, 0x73, 0x72, 0x00, 0x19, 0x63, 0x6e, 0x2e, 0x65,
        0x6e, 0x61, 0x69, 0x75, 0x6d, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x4d, 0x65,
        0x73, 0x73, 0x61, 0x67, 0x65, (byte) 0x95, (byte) 0xee, (byte) 0xd0, 0x49, (byte) 0xe2, 0x3a, (byte) 0xbe, 0x6d, 0x02, 0x00, 0x01,
        0x4c, 0x00, 0x04, 0x64, 0x61, 0x74, 0x61, 0x74, 0x00, 0x12, 0x4c, 0x6a, 0x61, 0x76, 0x61, 0x2f,
        0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3b, 0x78, 0x70, 0x74, 0x00,
        0x0b, 0x48, 0x45, 0x4c, 0x4c, 0x4f, 0x20, 0x57, 0x4f, 0x52, 0x4c, 0x44
}));

读取了92个字节,也成功解码

信息: [id: 0xembedded, L:embedded - R:embedded] READ: 92B
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 00 00 00 58 ac ed 00 05 73 72 00 19 63 6e 2e 65 |...X....sr..cn.e|
|00000010| 6e 61 69 75 6d 2e 6d 65 73 73 61 67 65 2e 4d 65 |naium.message.Me|
|00000020| 73 73 61 67 65 95 ee d0 49 e2 3a be 6d 02 00 01 |ssage...I.:.m...|
|00000030| 4c 00 04 64 61 74 61 74 00 12 4c 6a 61 76 61 2f |L..datat..Ljava/|
|00000040| 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 78 70 74 00 |lang/String;xpt.|
|00000050| 0b 48 45 4c 4c 4f 20 57 4f 52 4c 44             |.HELLO WORLD    |
+--------+-------------------------------------------------+----------------+
message = Message{data='HELLO WORLD'}

一个简单的协议就写好了

视频

源码

目录
相关文章
|
5月前
|
缓存 编解码 网络协议
Netty基础—8.Netty实现私有协议栈
本文系统性地阐述了私有协议栈的实现框架,首先介绍了私有协议的基本概念和通信模型,然后详细说明了协议栈的消息定义、链路建立与关闭机制,以及心跳检测、重连和重复登录保护等稳定性设计,接着重点描述了核心组件包括ChannelHandler体系、客户端与服务端架构以及会话ID处理器等关键模块,最后涵盖了数据包结构与编解码实现方案,整体呈现了从协议规范到具体功能实现的完整技术架构。
|
存储 数据安全/隐私保护
Netty实战(十三)WebSocket协议(一)
WebSocket 协议是完全重新设计的协议,旨在为 Web 上的双向数据传输问题提供一个切实可行的解决方案,使得客户端和服务器之间可以在任意时刻传输消息,因此,这也就要求它们异步地处理消息回执。
754 0
|
Java Spring
Springboot整合Netty,自定义协议实现
以上就是在Spring Boot中整合Netty并实现自定义协议的基本步骤。你需要根据你的自定义协议的具体需求,来实现你的编码器、解码器和处理器。
800 0
|
编解码
Netty Review - 优化Netty通信:如何应对粘包和拆包挑战_自定义长度分包编解码码器
Netty Review - 优化Netty通信:如何应对粘包和拆包挑战_自定义长度分包编解码码器
249 0
|
存储 编解码 Java
Netty使用篇:自定义编解码器
Netty使用篇:自定义编解码器
|
编解码 JSON 网络协议
Netty使用篇:Http协议编解码
Netty使用篇:Http协议编解码
|
Nacos
Netty自定义消息协议的实现逻辑处理粘包拆包、心跳机制
Netty自定义消息协议的实现逻辑处理粘包拆包、心跳机制
322 0
|
数据安全/隐私保护
Netty实战(十四)WebSocket协议(二)
我们之前说过为了将 ChannelHandler 安装到 ChannelPipeline 中,需要扩展了ChannelInitializer,并实现 initChannel()方法
402 0
|
JSON 网络协议 算法
由浅入深Netty协议设计与解析
由浅入深Netty协议设计与解析
230 0
|
JSON 网络协议 算法
Netty之协议设计
Netty之协议设计