概念
概述
Netty是一个高性能的网络应用程序框架,它提供了丰富的功能,包括编解码器,这些编解码器用于在网络中发送和接收数据时进行数据的编码和解码。
在Netty中,StringEncoder
和StringDecoder
是两个常用的编解码器,它们的功能和用途如下:
- StringEncoder:
- 功能:
StringEncoder
是一个字符编码器,它将字符串(String)数据转换为字节数组(byte[])。 - 用途:在网络通信中,数据传输是以字节流的形式进行的,因此,当需要发送文本数据时,需要将字符串转换为字节。
StringEncoder
就是执行这种转换的组件。 - 工作方式:它使用指定的字符集(如UTF-8)将字符串编码为字节。
- StringDecoder:
- 功能:
StringDecoder
是一个字符解码器,它将接收到的字节数组(byte[])数据转换为字符串(String)。 - 用途:当服务器或客户端接收到字节流数据时,需要将这些字节解码为文本格式,以便进行进一步处理。
StringDecoder
就是用来完成这个任务的。 - 工作方式:它使用指定的字符集(如UTF-8)将字节解码为字符串。
Netty 中的 StringEncoder
和 StringDecoder
是专门用于处理字符串数据的编码和解码器。这两个组件可以简化字符串在网络中的传输过程,让开发者能够更加方便地处理文本数据。
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 中,这样在数据接收过程中,接收到的字节流就会被自动解码为字符串。
总的来说,StringEncoder
和 StringDecoder
是 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)); } }
- 类继承关系:
StringEncoder
继承自MessageToMessageEncoder
,后者又继承自ChannelOutboundHandlerAdapter
。MessageToMessageEncoder
接口用于将消息对象转换为另一个消息对象,而这里的参数CharSequence
表示输入的消息对象类型,即字符序列。
- 构造函数:
StringEncoder()
:默认构造函数,使用默认字符集(UTF-8)。StringEncoder(Charset charset)
:指定字符集的构造函数。这里使用了ObjectUtil.checkNotNull
方法来检查字符集是否为空,保证字符集的有效性。
- 核心方法:
- encode(ChannelHandlerContext ctx, CharSequence msg, List out):将字符序列消息编码为字节。这个方法重写了MessageToMessageEncoder接口的encode方法。
- 在编码过程中,首先检查字符序列是否为空,如果为空,则直接返回。
- 使用ByteBufUtil.encodeString方法将字符序列编码为字节,并使用ctx.alloc()分配内存缓冲区。
- 将编码后的字节添加到输出列表out中,以便后续处理。
- 异常处理:
- 在encode方法中,如果发生异常,会抛出异常。
通过以上分析,我们可以看出StringEncoder的主要作用是将字符序列消息编码为字节。它利用指定的字符集进行编码,并在异常情况下抛出异常。这段代码简洁明了,实现了字符串编码的核心功能。 类定义:
StringDecoder
继承自MessageToMessageDecoder
,并实现了Sharable
接口。Sharable
接口主要用于实现解码器的共享,表示同一个解码器实例可以在不同的ChannelHandlerContext中使用。- 字段定义:StringDecoder类中有一个私有字段charset,它表示用于解码的字符集。
- 构造函数:StringDecoder类有两个构造函数,一个是无参构造函数,另一个是带有字符集参数的构造函数。无参构造函数会使用系统默认的字符集创建解码器实例,而有参构造函数允许用户指定字符集。
- decode方法:decode方法是MessageToMessageDecoder类的一个抽象方法,用于实现具体的解码逻辑。在StringDecoder中,它将接收到的字节缓冲区(ByteBuf)转换为字符串,并使用指定的字符集进行解码。最后,将解码后的字符串添加到解码消息列表中。
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源码的分析:
@Sharable public class StringDecoder extends MessageToMessageDecoder<ByteBuf> { • 1 • 2
private final Charset charset;
public StringDecoder() { this(Charset.defaultCharset()); } public StringDecoder(Charset charset) { this.charset = ObjectUtil.checkNotNull(charset, "charset"); }
@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中处理字符串数据编解码的关键组件,它们让字符串数据可以在网络中安全、准确地传输。