Netty使用篇:自定义编解码器_netty自定义编码器和解码器-程序员宅基地

技术标签: # Netty专栏  java  服务器  开发语言  

在这里插入图片描述

我们今天还是继续Netty,Netty的编码器和解码器就是Netty对Handler这个组件的一种使用场景而已,SpringWebFlex就是基于这个Netty来做的,在往上引深一层GateWay服务网关就是SpringWebFlex的实现,所以SpringCloud当中明确说明了:Gateway不能和SpringWebStarter一起使用,引入了Gateway就不能引入后者,因为这是两种实现策略,常见的WebStater是Tomcat+JavaEE这套实现方式实现的。而webflex是基于Netty,不从属于传统JavaEE的这种开发方式。
看了很多Netty为我们提供的编解码器之后,我们如何自定义一个编解码器呢?
自定义编解码之上,能够自定义一套自己的通信协议呢?
制定了自定义协议之后,自定义编解码器必须需要我们自己定义了。

第一章:自定义编解码器的流程

在我们现在的开发来讲呢,我们需要客户端,我们假定客户端开发也是基于Java,我们也会有服务端。客户端和服务器端是需要通信的会有数据的往来,客户端和服务端都基于Java开发的话,我们关注的点是Java的类型,换句话说就是一个一个的Java类。最终,我们的数据也会封装到一个一个的Java类当中,我们跟服务端通信,通信的主旨是什么?就是将客户端封装好的类的对象的数据发送给服务端,服务端处理好之后,将业务数据封装成对象将对象数据发送给客户端。我们不可能在两个虚拟机当中传递我们的Java对象,这个过程当中都是讲Java对象转换为ByteBuf,这是Netty提供的类型,作为Netty将其中的数据取出来转成Byte数组,基于Socket流发送出去,作为服务器端呢,Netty会将Byte数组中的二进制数据转换为ByteBuf,然后将ByteBuf转换为Java类型(业务相关的对象类型)。

以上两个过程中涉及到我们的两部分内容了。一部分内容是Java类型转换为ByteBuf在Nerry体系当中称之为编码,反之ByteBuf转换为Java类型在Netty体系当中称之为Netty的解码。
前边我们讲到的各种的Encoder和Decoder解决的都是这样的问题。剩余的通信问题都是Netty帮我们做了,我们只需要关注于自定义边编解码就可以了。
在这里插入图片描述

现在假设我们想要将“10-20”这样的数据编码成Long类型的数据,然后通过Netty发送给服务端。站在我们的服务端我们接收过来的数据是ByteBuf我们想要把他解码成Long类型。以上情况就涉及到编解码过程。显然,这个功能Netty是没有给我们提供的,我们基于此开发一个编码器和一个解码器。

我们现在想对任何一个框架进行一下拓展,都需要使用当前框架提供的规范进行开发。这个规范指的就是框架提供各种各种的接口或者父类。我们遵循这些规范即可,框架自身也会遵循这些规范。

作为编码器来讲,他需要继承一个父类:MessageToByteEncoder,解码器需要继承一个父类:ByteToMessageDecoder,最后将我们开发的边解码器假如到客户端或者服务端的pipeLine当中。

我们这里边发送的是要给字符串,创建一个类MyLongToByteEncoder extends MessageToByteEncoder去继承这个类之后,我们实现encoder方法,这个ctx是整个pileLine的上线文,是整个PipeLine的一个核心实现,获得了ctx就等于拿到了这个channel当中的pipeLine中的所有信息。这个ctx当中都有啥,channel和ByteBuf以及各种各样的Handler。msg带表了,客户端client要输出的内容,对于我们这个案例来讲我们在这里输出的就是10-20的数据。第三个参数ByteBufout就代表了就真正的往服务端写数据的ByteBuf,我们转换好的数据好存放到这里边。

我们基于循环的方式将每一个Long类型的数据发送出去。这样我们的客户端就发送出去了。这里有写以为,我们之前不都是使用一个内部类吗?为什么这里不用匿名内部类了。为什么现在不用了,首先从设计的角度来讲,我们要尽可能的少使用这个内部类,因为内部类的重用性很差,所以一个类型要复用话,我们尽量不用内部类,除非这个类的使用范围仅限于本类,这个时候我们才会考虑使用内部类,这是一种变量的封装。最典型的内部类体现封装概念的案例就是Map.Entry<key,value> 实际上做内部类就不是一个地道的方式,后续开发过程中要少用内部类。

服务器端,我们先使用一个LongHandler进行解码,将ByteBuf转换为Long(一个Long在ByteBuf当中占用8个字节)然后添加一个自定义的解码器,将数据存存储到这里边。所以这个List的泛型是Object

客户端代码:

public class MyNettyClient {
    public static void main(String[] args) throws InterruptedException{
        log.debug("myNettyClientStarter------");
        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.channel(NioSocketChannel.class);
        Bootstrap group = bootstrap.group(eventLoopGroup);
        bootstrap.handler(new ChannelInitializer<NioSocketChannel>() {
            @Override
            protected void initChannel(NioSocketChannel ch) throws Exception {
                ch.pipeline().addLast(new LoggingHandler());
                //自定义编码器
                //Encoderxxx
                ch.pipeline().addLast(new MyLongToByteEncoder());
                //编解码器 或者 handler 匿名的内部类 重用性差
                //内部类 ---> 使用范围 仅限于本类 (封装) Map  Map.Entry(key -- value)
            }
        });
        Channel channel = bootstrap.connect(new InetSocketAddress(8000)).sync().channel();
        channel.writeAndFlush("10-20");

    }
}

public class MyLongToByteEncoder extends MessageToByteEncoder<String>{

    private static final Logger log = LoggerFactory.getLogger(MyLongToByteEncoder.class);

    @Override
    //获得了ctx等于拿到了这个channel相关的pipeline中的所有信息
    //1. channel
    //2. ByteBuf
    // String msg 编码器接受的 client输出的内容
    //ByteBuf out 真正往服务端写的ByteBuf的数据,细节  xxx
    protected void encode(ChannelHandlerContext ctx, String msg, ByteBuf out) throws Exception {
        log.debug("encode method invoke  ");
        String[] messges = msg.split("-");
        for (String messge : messges) {
            long resultLong = Long.parseLong(messge);
            //每一个long类型的数据,在bytebuf中占用8个字节
            out.writeLong(resultLong);
        }
    }
}

客户端日志:

2022-11-24 22:24:46.048 [nioEventLoopGroup-2-1] DEBUG io.netty.buffer.AbstractByteBuf - -Dio.netty.buffer.checkBounds: true
2022-11-24 22:24:46.049 [nioEventLoopGroup-2-1] DEBUG io.netty.util.ResourceLeakDetectorFactory - Loaded default ResourceLeakDetector: io.netty.util.ResourceLeakDetector@2ac301e9
2022-11-24 22:24:46.052 [nioEventLoopGroup-2-1] DEBUG com.suns.netty10.MyLongToByteEncoder - encode method invoke  
2022-11-24 22:24:46.054 [nioEventLoopGroup-2-1] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x3c44ede1, L:/192.168.1.4:53328 - R:0.0.0.0/0.0.0.0:8000] WRITE: 16B
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 00 00 00 00 00 00 00 0a 00 00 00 00 00 00 00 14 |................|
+--------+-------------------------------------------------+----------------+
2022-11-24 22:24:46.054 [nioEventLoopGroup-2-1] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x3c44ede1, L:/192.168.1.4:53328 - R:0.0.0.0/0.0.0.0:8000] FLUSH

服务端代码:

public class MyNettyServer {
    public static void main(String[] args) {
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.channel(NioServerSocketChannel.class);
        serverBootstrap.group(new NioEventLoopGroup());
        serverBootstrap.childHandler(new ChannelInitializer<NioSocketChannel>() {
            @Override
            //
            protected void initChannel(NioSocketChannel ch) throws Exception {
                ChannelPipeline pipeline = ch.pipeline();
                pipeline.addLast(new LoggingHandler());
                //ByteBuf --- Long
                //解码过程
                pipeline.addLast(new MyByteToLongDecoder());
                //pipeline.addLast(new MyLongCodec());
                pipeline.addLast(new ChannelInboundHandlerAdapter(){
                    @Override
                    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                          log.debug("recive date in handler ...");
                          if(msg instanceof Long){
                              Long result = (Long) msg;
                              log.debug("my handler data is {} ",result);
                          }
                    }
                });
            }
        });
        //
        serverBootstrap.bind(8000);
    }
}

public class MyByteToLongDecoder extends ByteToMessageDecoder {
    @Override
    //获得了ctx等于拿到了这个channel相关的pipeline中的所有信息
    //1. channel
    //2. ByteBuf
    //ByteBuf in client提交上来的数据
    // decode方法处理的过程中,如果bytebuf没有处理完,那么他会重复调用decode方法
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        log.debug("decode method invoke ...");//ssss
        if (in.readableBytes() >= 8) {
            in.markReaderIndex();
                long reciveLong = in.readLong();
                out.add(reciveLong);
        }
        System.out.println(ByteBufUtil.prettyHexDump(in));
    }
}

服务端日志:

2022-11-24 22:24:46.064 [nioEventLoopGroup-2-2] DEBUG io.netty.util.ResourceLeakDetectorFactory - Loaded default ResourceLeakDetector: io.netty.util.ResourceLeakDetector@53fb3f15
2022-11-24 22:24:46.067 [nioEventLoopGroup-2-2] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x57544fbc, L:/192.168.1.4:8000 - R:/192.168.1.4:53328] READ: 16B
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 00 00 00 00 00 00 00 0a 00 00 00 00 00 00 00 14 |................|
+--------+-------------------------------------------------+----------------+
2022-11-24 22:24:46.073 [nioEventLoopGroup-2-2] DEBUG com.suns.netty10.MyByteToLongDecoder - decode method invoke ...
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 00 00 00 00 00 00 00 14                         |........        |
+--------+-------------------------------------------------+----------------+
2022-11-24 22:24:46.074 [nioEventLoopGroup-2-2] DEBUG com.suns.netty10.MyNettyServer - recive date in handler ...
2022-11-24 22:24:46.074 [nioEventLoopGroup-2-2] DEBUG com.suns.netty10.MyNettyServer - my handler data is 10 
2022-11-24 22:24:46.074 [nioEventLoopGroup-2-2] DEBUG com.suns.netty10.MyByteToLongDecoder - decode method invoke ...

2022-11-24 22:24:46.074 [nioEventLoopGroup-2-2] DEBUG com.suns.netty10.MyNettyServer - recive date in handler ...
2022-11-24 22:24:46.074 [nioEventLoopGroup-2-2] DEBUG com.suns.netty10.MyNettyServer - my handler data is 20 

站在客户端角度来讲,客户端是使用一次给我们发送过来的,16个字节一次给发过来的。但是为什么站在服务端解码的时候要调用两个decode方法,产生了两个Message。(整个PipeLine的调用次数是按照Message个数定义的)但是消息我认可是两个,但是为啥要decode方法两次呢?这个是在Netty体系当中一个很大的坑,如果在Netty当中的ByteBuf当中如果一次性没处理完。他就会再次调用decode方法。再次交给你处理,恰好此时我们的数据时候16个字节,我们这就取了8个字节,这个时候我们的就调用了两次,如果是20个字节,我们的decode方法被调用了三次,这个是以处理完没处理完ByteBuf当中的数据当做一回事的。readLong读了8个字节,后边还有8个字节。
decode方法外部包了一层循环,只要是我们的ByteBuf当中还有数据没有读完。就会重复的调用decode方法,接着去处理。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Facial_Mask/article/details/127991656

智能推荐

oracle 12c 集群安装后的检查_12c查看crs状态-程序员宅基地

文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态

解决jupyter notebook无法找到虚拟环境的问题_jupyter没有pytorch环境-程序员宅基地

文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境

国内安装scoop的保姆教程_scoop-cn-程序员宅基地

文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn

Element ui colorpicker在Vue中的使用_vue el-color-picker-程序员宅基地

文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker

迅为iTOP-4412精英版之烧写内核移植后的镜像_exynos 4412 刷机-程序员宅基地

文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机

Linux系统配置jdk_linux配置jdk-程序员宅基地

文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk

随便推点

matlab(4):特殊符号的输入_matlab微米怎么输入-程序员宅基地

文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入

C语言程序设计-文件(打开与关闭、顺序、二进制读写)-程序员宅基地

文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。‍ Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。

Touchdesigner自学笔记之三_touchdesigner怎么让一个模型跟着鼠标移动-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动

【附源码】基于java的校园停车场管理系统的设计与实现61m0e9计算机毕设SSM_基于java技术的停车场管理系统实现与设计-程序员宅基地

文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计

Android系统播放器MediaPlayer源码分析_android多媒体播放源码分析 时序图-程序员宅基地

文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;amp;gt;Jni-&amp;amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图

java 数据结构与算法 ——快速排序法-程序员宅基地

文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法