Netty Review - StringEncoder字符串编码器和StringDecoder 解码器的使用与源码解读

简介: Netty Review - StringEncoder字符串编码器和StringDecoder 解码器的使用与源码解读


概念


概述

Netty是一个高性能的网络应用程序框架,它提供了丰富的功能,包括编解码器,这些编解码器用于在网络中发送和接收数据时进行数据的编码和解码。

在Netty中,StringEncoderStringDecoder是两个常用的编解码器,它们的功能和用途如下:

  1. StringEncoder
  • 功能StringEncoder是一个字符编码器,它将字符串(String)数据转换为字节数组(byte[])。
  • 用途:在网络通信中,数据传输是以字节流的形式进行的,因此,当需要发送文本数据时,需要将字符串转换为字节。StringEncoder就是执行这种转换的组件。
  • 工作方式:它使用指定的字符集(如UTF-8)将字符串编码为字节。
  1. StringDecoder
  • 功能StringDecoder是一个字符解码器,它将接收到的字节数组(byte[])数据转换为字符串(String)。
  • 用途:当服务器或客户端接收到字节流数据时,需要将这些字节解码为文本格式,以便进行进一步处理。StringDecoder就是用来完成这个任务的。
  • 工作方式:它使用指定的字符集(如UTF-8)将字节解码为字符串。

Netty 中的 StringEncoderStringDecoder 是专门用于处理字符串数据的编码和解码器。这两个组件可以简化字符串在网络中的传输过程,让开发者能够更加方便地处理文本数据。

StringEncoder

StringEncoder 是一个将字符串编码为字节流的组件。在 Netty 的 pipeline 中,当你需要将字符串发送到网络时,你可以使用 StringEncoder 来实现。它会将字符串转换为字节流,以便可以在网络中传输。

例如,当你使用 Netty 的 Bootstrap 类来配置你的客户端时,你可以为你的 channel pipeline 添加一个 StringEncoder

Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
       .channel(NioSocketChannel.class)
       .handler(new ChannelInitializer<SocketChannel>() {
           @Override
           public void initChannel(SocketChannel ch) throws Exception {
               ch.pipeline().addLast(new StringEncoder());
               // 添加其他 handlers...
           }
       });

在这个例子中,StringEncoder 被添加到了 channel 的 pipeline 中,这样在数据传输过程中,发送的字符串就会被自动编码为字节流。


StringDecoder

StringEncoder 相对应,StringDecoder 是用于将接收到的字节流解码为字符串的组件。当你在 Netty 的 pipeline 中接收到字节流时,你可以使用 StringDecoder 来自动将字节流转换为字符串。

继续上面的例子,如果你想在 pipeline 中添加 StringDecoder,你可以这样做:

Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
       .channel(NioSocketChannel.class)
       .handler(new ChannelInitializer<SocketChannel>() {
           @Override
           public void initChannel(SocketChannel ch) throws Exception {
               ch.pipeline().addLast(new StringDecoder());
               // 添加其他 handlers...
           }
       });

在这个例子中,StringDecoder 被添加到了 channel 的 pipeline 中,这样在数据接收过程中,接收到的字节流就会被自动解码为字符串。

总的来说,StringEncoderStringDecoder 是 Netty 中专门用于处理字符串数据的编码和解码器,它们简化了字符串在网络中的传输过程,让开发者能够更加方便地处理文本数据。


Code

package com.artisan.codec.stringencoder;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
public class NettyServer {
    // Netty服务器的主方法
    public static void main(String[] args) throws Exception {
        // 创建boss线程组,用于接受客户端连接
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        // 创建worker线程组,用于进行SocketChannel的数据读写
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            // 创建ServerBootstrap,它是Netty服务端的启动助手
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            // 绑定线程组到ServerBootstrap
            serverBootstrap.group(bossGroup, workerGroup)
                    // 指定使用NioServerSocketChannel接受进来的连接
                    .channel(NioServerSocketChannel.class)
                    // 设置子通道处理器
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            // 获取通道的管道
                            ChannelPipeline pipeline = ch.pipeline();
                            // 添加字符串解码器
                            pipeline.addLast(new StringDecoder());
                            // 添加自定义的处理器
                            pipeline.addLast(new NettyServerHandler());
                        }
                    });
            // 打印服务启动信息
            System.out.println("netty server start。。");
            // 绑定端口号,并同步等待成功,即启动服务端
            ChannelFuture channelFuture = serverBootstrap.bind(1234).sync();
            // 等待服务器socket关闭
            channelFuture.channel().closeFuture().sync();
        } finally {
            // 优雅地关闭boss线程组
            bossGroup.shutdownGracefully();
            // 优雅地关闭worker线程组
            workerGroup.shutdownGracefully();
        }
    }
}
package com.artisan.codec.stringencoder;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("从客户端读取到String:" + msg.toString());
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

这段代码是一个简单的Netty服务器启动类,其中包括了启动Netty服务器的所有必要步骤。代码中包含了中文注释,对每一部分的功能都做了简单解释。


package com.artisan.codec.stringencoder;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
    // 当读取到客户端发送的消息时,会调用此方法
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 打印从客户端读取到的字符串消息
        System.out.println("从客户端读取到String:" + msg.toString());
    }
    // 当发生异常时,会调用此方法
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        // 打印异常堆栈信息
        cause.printStackTrace();
        // 关闭通道
        ctx.close();
    }
}

package com.artisan.codec.stringencoder;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringEncoder;
/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
public class NettyClient {
    // Netty客户端的主方法
    public static void main(String[] args) throws Exception {
        // 创建事件循环组
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            // 创建Bootstrap,它是Netty客户端的启动助手
            Bootstrap bootstrap = new Bootstrap();
            // 绑定事件循环组到Bootstrap
            bootstrap.group(group).channel(NioSocketChannel.class)
                    // 设置通道初始化处理器
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            // 获取通道的管道
                            ChannelPipeline pipeline = ch.pipeline();
                            // 添加字符串编码器
                            pipeline.addLast(new StringEncoder());
                            // 添加自定义的处理器
                            pipeline.addLast(new NettyClientHandler());
                        }
                    });
            // 打印客户端启动信息
            System.out.println("netty client start。。");
            // 连接到服务器,同步等待成功,即启动客户端
            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 1234).sync();
            // 等待客户端socket关闭
            channelFuture.channel().closeFuture().sync();
        } finally {
            // 优雅地关闭事件循环组
            group.shutdownGracefully();
        }
    }
}

这段代码是一个简单的Netty客户端启动类,其中包括了启动Netty客户端的所有必要步骤。代码中包含了中文注释,对每一部分的功能都做了简单解释。

package com.artisan.codec.stringencoder;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
    // 当从服务器接收到消息时,会调用此方法
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 打印接收到的服务器消息
        System.out.println("收到服务器消息:" + msg);
    }
    // 当通道激活时,会调用此方法
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // 定义要发送的字符串消息
        String msg = "字符串消息";
        // 打印发送消息的日志信息
        System.out.println("NettyClientHandler发送数据:" + msg);
        // 向服务器发送消息
        ctx.writeAndFlush(msg);
    }
}

这段代码是一个自定义的Netty客户端处理器,继承自ChannelInboundHandlerAdapter。其中包括了两个重写的方法,channelRead和channelActive,分别用于处理服务器消息的读取和通道激活时发送消息。代码中包含了中文注释,对每一部分的功能都做了简单解释


【测试结果】


源码分析

StringEncoder

// StringEncoder类继承自MessageToMessageEncoder,用于将字符序列(CharSequence)消息转换为另一个消息类型
@Sharable
public class StringEncoder extends MessageToMessageEncoder<CharSequence> {
    // 用于编码的字符集
    private final Charset charset;
    /**
     * 使用当前系统的默认字符集创建一个新的实例。
     */
    public StringEncoder() {
        this(Charset.defaultCharset());
    }
    /**
     * 使用指定的字符集创建一个新的实例。
     */
    public StringEncoder(Charset charset) {
        this.charset = ObjectUtil.checkNotNull(charset, "charset");
    }
    // 重写父类的encode方法,实现字符序列到字节的转换
    @Override
    protected void encode(ChannelHandlerContext ctx, CharSequence msg, List<Object> out) throws Exception {
        // 如果字符序列为空,则直接返回
        if (msg.length() == 0) {
            return;
        }
        // 使用ByteBufUtil.encodeString方法将字符序列编码为字节,并使用ctx.alloc()分配内存缓冲区
        // 然后将编码后的字节添加到输出列表out中,以便后续处理
        out.add(ByteBufUtil.encodeString(ctx.alloc(), CharBuffer.wrap(msg), charset));
    }
}
  1. 类继承关系
  • StringEncoder继承自MessageToMessageEncoder,后者又继承自ChannelOutboundHandlerAdapterMessageToMessageEncoder接口用于将消息对象转换为另一个消息对象,而这里的参数CharSequence表示输入的消息对象类型,即字符序列。
  1. 构造函数
  • StringEncoder():默认构造函数,使用默认字符集(UTF-8)。
  • StringEncoder(Charset charset):指定字符集的构造函数。这里使用了ObjectUtil.checkNotNull方法来检查字符集是否为空,保证字符集的有效性。
  1. 核心方法
  • encode(ChannelHandlerContext ctx, CharSequence msg, List out):将字符序列消息编码为字节。这个方法重写了MessageToMessageEncoder接口的encode方法。
  • 在编码过程中,首先检查字符序列是否为空,如果为空,则直接返回。
  • 使用ByteBufUtil.encodeString方法将字符序列编码为字节,并使用ctx.alloc()分配内存缓冲区。
  • 将编码后的字节添加到输出列表out中,以便后续处理。
    1. 异常处理:
    • 在encode方法中,如果发生异常,会抛出异常。
      通过以上分析,我们可以看出StringEncoder的主要作用是将字符序列消息编码为字节。它利用指定的字符集进行编码,并在异常情况下抛出异常。这段代码简洁明了,实现了字符串编码的核心功能。

    StringDecoder

    /**
     * 字符串解码器,继承自Netty的MessageToMessageDecoder,用于将ByteBuf消息解码为字符串消息。
     */
    @Sharable
    public class StringDecoder extends MessageToMessageDecoder<ByteBuf> {
        // 字符集解码器,用于解码ByteBuf中的数据为字符串
        private final Charset charset;
        /**
         * 使用默认的系统字符集创建一个新的解码器实例。
         */
        public StringDecoder() {
            this(Charset.defaultCharset());
        }
        /**
         * 使用指定的字符集创建一个新的解码器实例。
         */
        public StringDecoder(Charset charset) {
            this.charset = ObjectUtil.checkNotNull(charset, "charset");
        }
        /**
         * 重写decode方法,将ByteBuf中的数据解码为字符串,并添加到解码消息列表中。
         */
        @Override
        protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
            // 将ByteBuf转换为字符串,并使用指定的字符集解码
            out.add(msg.toString(charset));
        }
    }
    

    StringDecoder是Netty网络通信框架中的一个解码器,用于将接收到的字节缓冲区(ByteBuf)解码为字符串。下面是对StringDecoder源码的分析:

    1. 类定义:StringDecoder继承自MessageToMessageDecoder,并实现了Sharable接口。Sharable接口主要用于实现解码器的共享,表示同一个解码器实例可以在不同的ChannelHandlerContext中使用。
    @Sharable
    public class StringDecoder extends MessageToMessageDecoder<ByteBuf> {
    • 1
    • 2

    1. 字段定义:StringDecoder类中有一个私有字段charset,它表示用于解码的字符集。
    private final Charset charset;
    
    1. 构造函数:StringDecoder类有两个构造函数,一个是无参构造函数,另一个是带有字符集参数的构造函数。无参构造函数会使用系统默认的字符集创建解码器实例,而有参构造函数允许用户指定字符集。
    public StringDecoder() {
        this(Charset.defaultCharset());
    }
    public StringDecoder(Charset charset) {
        this.charset = ObjectUtil.checkNotNull(charset, "charset");
    }
    
    1. decode方法:decode方法是MessageToMessageDecoder类的一个抽象方法,用于实现具体的解码逻辑。在StringDecoder中,它将接收到的字节缓冲区(ByteBuf)转换为字符串,并使用指定的字符集进行解码。最后,将解码后的字符串添加到解码消息列表中。
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
        out.add(msg.toString(charset));
    }
    

    总结:StringDecoder是Netty中的一个解码器,用于将接收到的字节缓冲区(ByteBuf)解码为字符串。它提供了两个构造函数,允许用户指定字符集。在decode方法中,它将ByteBuf转换为字符串,并使用指定的字符集进行解码,将解码后的字符串添加到解码消息列表中。


    小结

    在Netty的通道处理器(ChannelHandler)中,StringEncoder和StringDecoder通常以管道(Pipeline)的形式添加到通道(Channel)中,这样,在数据传输过程中,发送的数据会被StringEncoder编码,接收的数据会被StringDecoder解码。通过这种方式,Netty保证了字符串数据在网络中的高效传输和正确解析。

    简而言之,StringEncoder和StringDecoder是Netty中处理字符串数据编解码的关键组件,它们让字符串数据可以在网络中安全、准确地传输。


相关文章
|
7月前
|
编解码 开发者
Netty Review - 深入理解Netty: ChannelHandler的生命周期与事件处理机制
Netty Review - 深入理解Netty: ChannelHandler的生命周期与事件处理机制
149 0
|
7月前
|
前端开发 UED
Netty Review - Netty自动重连机制揭秘:原理与最佳实践
Netty Review - Netty自动重连机制揭秘:原理与最佳实践
243 0
|
7月前
|
监控 网络协议 调度
Netty Review - 深入探讨Netty的心跳检测机制:原理、实战、IdleStateHandler源码分析
Netty Review - 深入探讨Netty的心跳检测机制:原理、实战、IdleStateHandler源码分析
453 0
|
7月前
|
编解码
Netty Review - 优化Netty通信:如何应对粘包和拆包挑战_自定义长度分包编解码码器
Netty Review - 优化Netty通信:如何应对粘包和拆包挑战_自定义长度分包编解码码器
102 0
|
7月前
|
网络协议
Netty Review - 优化Netty通信:如何应对粘包和拆包挑战
Netty Review - 优化Netty通信:如何应对粘包和拆包挑战
108 0
|
7月前
|
缓存 前端开发 Java
Netty Review - Netty与Protostuff:打造高效的网络通信
Netty Review - Netty与Protostuff:打造高效的网络通信
98 0
|
7月前
|
编解码 前端开发 网络协议
Netty Review - ObjectEncoder对象和ObjectDecoder对象解码器的使用与源码解读
Netty Review - ObjectEncoder对象和ObjectDecoder对象解码器的使用与源码解读
170 0
|
存储 缓存 NoSQL
跟着源码学IM(十一):一套基于Netty的分布式高可用IM详细设计与实现(有源码)
本文将要分享的是如何从零实现一套基于Netty框架的分布式高可用IM系统,它将支持长连接网关管理、单聊、群聊、聊天记录查询、离线消息存储、消息推送、心跳、分布式唯一ID、红包、消息同步等功能,并且还支持集群部署。
13521 1
|
7月前
|
消息中间件 Oracle Dubbo
Netty 源码共读(一)如何阅读JDK下sun包的源码
Netty 源码共读(一)如何阅读JDK下sun包的源码
140 1
|
NoSQL Java Redis
跟着源码学IM(十二):基于Netty打造一款高性能的IM即时通讯程序
关于Netty网络框架的内容,前面已经讲了两个章节,但总归来说难以真正掌握,毕竟只是对其中一个个组件进行讲解,很难让诸位将其串起来形成一条线,所以本章中则会结合实战案例,对Netty进行更深层次的学习与掌握,实战案例也并不难,一个非常朴素的IM聊天程序。 原本打算做个多人斗地主练习程序,但那需要织入过多的业务逻辑,因此一方面会带来不必要的理解难度,让案例更为复杂化,另一方面代码量也会偏多,所以最终依旧选择实现基本的IM聊天程序,既简单,又能加深对Netty的理解。
175 1