netty案例,netty4.1基础入门篇九《自定义编码解码器》

前言介绍

在实际应用场景里,只要是支持sokcet通信的都可以和Netty交互,比如中继器、下位机、PLC等。这些场景下就非常需要自定义编码解码器,来处理字节码传输,并控制半包、粘包以及安全问题。那么本章节我们通过实现ByteToMessageDecoder、MessageToByteEncoder来实现我们的需求。

环境准备

1、jdk1.8【jdk1.7以下只能部分支持netty】 2、Netty4.1.36.Final【netty3.x 4.x 5每次的变化较大,接口类名也随着变化】 3、telnet 测试【可以现在你的win7机器上测试这个命令,用于链接到服务端的测试命令】

代码示例

itstack-demo-netty-1-09
└── src
    ├── main
    │   └── java
    │       └── org.itstack.demo.netty
    │			├── codec
    │           │ 	├── MyDecoder.java
    │           │ 	└── MyEncoder.java
    │			└── server
    │           	├── MyChannelInitializer.java
    │           	├── MyServerHandler.java
    │           	└── NettyServer.java
    └── test
         └── java
             └── org.itstack.demo.test
                 └── ApiTest.java

>MyDecoder.java *用于处理解码,02开始 03结束

/**
 * 自定义解码器
 * 虫洞栈:https://bugstack.cn
 * 公众号:bugstack虫洞栈  {关注获取学习源码}
 * 虫洞群:①群5398358 ②群5360692
 * Create by fuzhengwei on 2019
 */
public class MyDecoder extends ByteToMessageDecoder {

    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf in, List<object> out) throws Exception {
        int size = in.readableBytes();

        byte[] data = new byte[size];
        in.readBytes(data);

        byte begin = data[0];     //开始符02
        byte end = data[size - 1];//结束符03

        //无开始符,只有结束符,数据丢包
        if (begin != 0x02 &amp;&amp; end == 0x03){
            System.out.println("公众号:bugstack虫洞栈 提示;byteBuf数据,无开始符,只有结束符,数据丢包。");
            channelHandlerContext.writeAndFlush("error");
            return; //直接返回,不置位指针
        }
        //有开始符,无结束符号,数据半包。置位指针,接收余下数据
        if (begin != 0x02 || end != 0x03) {
            in.resetReaderIndex();
            System.out.println("公众号:bugstack虫洞栈 提示;byteBuf数据,有开始符,无结束符号,数据半包。置位指针,接收余下数据。");
            return;
        }
        //数据完整,解析处理
        System.out.println(JSON.toJSONString(data));
        //越过02 03位
        ByteBuf copy = in.copy(1, size - 1);
        //转换
        String msg = copy.toString(Charset.forName("GBK"));
        //填充
        out.add(msg);
    }

}

>MyEncoder.java *用于处理编码,在byte开始和结束加上02 03

/**
 * 自定义编码器
 * 虫洞栈:https://bugstack.cn
 * 公众号:bugstack虫洞栈  {关注获取学习源码}
 * 虫洞群:①群5398358 ②群5360692
 * Create by fuzhengwei on 2019
 */
public class MyEncoder extends MessageToByteEncoder {

    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, Object in, ByteBuf out) throws Exception {

        String msg = in.toString();
        byte[] bytes = msg.getBytes();

        byte[] send = new byte[bytes.length + 2];
        System.arraycopy(bytes, 0, send, 1, bytes.length);
        send[0] = 0x02;
        send[send.length - 1] = 0x03;
        
        out.writeInt(send.length);
        out.writeBytes(send);

    }

}

>MyChannelInitializer.java

/**
 * 虫洞栈:https://bugstack.cn
 * 公众号:bugstack虫洞栈  {关注获取学习源码}
 * 虫洞群:①群5398358 ②群5360692
 * Create by fuzhengwei on 2019
 */
public class MyChannelInitializer extends ChannelInitializer<socketchannel> {

    @Override
    protected void initChannel(SocketChannel channel) {
        //自定义解码器
        channel.pipeline().addLast(new MyDecoder());
        //自定义编码器
        channel.pipeline().addLast(new MyEncoder());
        //在管道中添加我们自己的接收数据实现方法
        channel.pipeline().addLast(new MyServerHandler());
    }

}

>MyServerHandler.java

/**
 * 虫洞栈:https://bugstack.cn
 * 公众号:bugstack虫洞栈  {关注获取学习源码}
 * 虫洞群:①群5398358 ②群5360692
 * Create by fuzhengwei on 2019
 */
public class MyServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        SocketChannel channel = (SocketChannel) ctx.channel();
        System.out.println("链接报告开始 {公众号:bugstack虫洞栈 &gt;获取学习源码}");
        System.out.println("链接报告信息:有一客户端链接到本服务端");
        System.out.println("链接报告IP:" + channel.localAddress().getHostString());
        System.out.println("链接报告Port:" + channel.localAddress().getPort());
        System.out.println("链接报告完毕");
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        //接收msg消息{与上一章节相比,此处已经不需要自己进行解码}
        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " 接收到消息:" + msg);

        ctx.writeAndFlush("hi I'm ok");
    }

}

测试结果

>启动NettyServer

itstack-demo-netty server start done. {关注公众号:bugstack虫洞栈,获取源码}
链接报告开始 {公众号:bugstack虫洞栈 &gt;获取学习源码}
链接报告信息:有一客户端链接到本服务端
链接报告IP:192.168.1.121
链接报告Port:7397
链接报告完毕
"AmhpILmr1tq6xaO6YnVnc3RhY2uz5ra01bsgAw=="
2019-08-18 15:32:47 接收到消息:hi 公众号:bugstack虫洞栈 
"AmhpILmr1tq6xaO6YnVnc3RhY2uz5ra01bsgAw=="
2019-08-18 15:32:48 接收到消息:hi 公众号:bugstack虫洞栈 
"AmhpILmr1tq6xaO6YnVnc3RhY2uz5ra01bsgAw=="
2019-08-18 15:32:49 接收到消息:hi 公众号:bugstack虫洞栈 
"AmhpILmr1tq6xaO6YnVnc3RhY2uz5ra01bsgAw=="
2019-08-18 15:32:49 接收到消息:hi 公众号:bugstack虫洞栈 
"AmhpILmr1tq6xaO6YnVnc3RhY2uz5ra01bsgAw=="
2019-08-18 15:32:50 接收到消息:hi 公众号:bugstack虫洞栈 
"AmhpILmr1tq6xaO6YnVnc3RhY2uz5ra01bsgAw=="
2019-08-18 15:32:51 接收到消息:hi 公众号:bugstack虫洞栈 
"AmhpILmr1tq6xaO6YnVnc3RhY2uz5ra01bsgAw=="
2019-08-18 15:32:51 接收到消息:hi 公众号:bugstack虫洞栈 

Process finished with exit code -1

>启动模拟器NetAssist,用TcpClient链接服务端


微信公众号:bugstack虫洞栈,欢迎您的关注&获取源码!

微信公众号:bugstack虫洞栈,欢迎您的关注&获取源码!

</socketchannel></object>

转载于:https://my.oschina.net/itstack/blog/3096094

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Netty框架提供了很多编码器和解码器,但有时候我们需要自定义编码器和解码器,以满足特定的业务需求。在Netty中,自定义编码器和解码器非常简单,只需要继承ByteToMessageDecoder或MessageToByteEncoder,并实现其中的抽象方法即可。 以下是一个自定义编码器的示例代码: ```java public class MyEncoder extends MessageToByteEncoder<MyMessage> { @Override protected void encode(ChannelHandlerContext ctx, MyMessage msg, ByteBuf out) throws Exception { byte[] data = msg.getData(); out.writeInt(data.length); out.writeBytes(data); } } ``` 在这个示例中,我们定义了一个名为MyEncoder的编码器,它继承自MessageToByteEncoder,并实现了encode方法。在encode方法中,我们首先获取消息的数据,然后将消息的数据长度写入到ByteBuf中,最后将消息的数据写入到ByteBuf中。 以下是一个自定义解码器的示例代码: ```java public class MyDecoder extends ByteToMessageDecoder { @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { if (in.readableBytes() < 4) { return; } int length = in.readInt(); if (in.readableBytes() < length) { in.resetReaderIndex(); return; } byte[] data = new byte[length]; in.readBytes(data); MyMessage message = new MyMessage(length, data); out.add(message); } } ``` 在这个示例中,我们定义了一个名为MyDecoder的解码器,它继承自ByteToMessageDecoder,并实现了decode方法。在decode方法中,我们首先判断ByteBuf中是否有足够的字节可读,如果不够则直接返回。然后从ByteBuf中读取消息的长度和数据,并将它们封装成MyMessage对象,加入到解码结果列表中。 在使用自定义编码器和解码器时,只需要将它们注册到ChannelPipeline中即可。以下是注册的示例代码: ```java ChannelPipeline pipeline = channel.pipeline(); pipeline.addLast(new MyDecoder()); pipeline.addLast(new MyEncoder()); ``` 这样,在ChannelPipeline中的所有ChannelHandler都可以使用MyMessage对象,而无需关心它们与字节流的转换过程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值