写点什么

netty 系列之:netty 中常用的字符串编码解码器

作者:程序那些事
  • 2022 年 4 月 21 日
  • 本文字数:2457 字

    阅读完需:约 8 分钟

netty系列之:netty中常用的字符串编码解码器

简介

字符串是我们程序中最常用到的消息格式,也是最简单的消息格式,但是正因为字符串 string 太过简单,不能附加更多的信息,所以在 netty 中选择的是使用 byteBuf 作为最底层的消息传递载体。


虽然底层使用的 ByteBuf,但是对于程序员来说,还是希望能够使用这种最简单的字符串格式,那么有什么简单的方法吗?

netty 中的字符串编码解码器

为了解决在 netty 的 channel 中传递字符串的问题,netty 提供了针对于字符串的编码和解码器,分别是 StringEncoder 和 StringDecoder。


我们来看下他们是怎么在程序中使用的,首先是将 StringDecoder 和 StringEncoder 加入 channelPipeline 中:


   ChannelPipeline pipeline = ...;     // Decoders   pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(80));   pipeline.addLast("stringDecoder", new StringDecoder(CharsetUtil.UTF_8));     // Encoder   pipeline.addLast("stringEncoder", new StringEncoder(CharsetUtil.UTF_8));
复制代码


注意,这里我们在使用 StringDecoder 之前还调用了 LineBasedFrameDecoder,先把数据按行进行分割,然后再进行字符串的读取。


那么有人要问了,decoder 加入了 LineBasedFrameDecoder 预处理,为什么写入的时候没有添加行的分割符呢?


事实上这里有两种处理方式,第一种就是在向 channel 中写入字符串的时候,手动加上行分隔符,如下所示:


   void channelRead(ChannelHandlerContext ctx, String msg) {       ch.write("Did you say '" + msg + "'?\n");   }
复制代码


如果不想每次都在 msg 后面加上换行符,那么可以将 StringEncoder 替换成为 LineEncoder,上面的 pipeline 就变成下面这样:


   ChannelPipeline pipeline = ...;     // Decoders   pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(80));   pipeline.addLast("stringDecoder", new StringDecoder(CharsetUtil.UTF_8));     // Encoder   pipeline.addLast("lineEncoder", new LineEncoder(LineSeparator.UNIX, CharsetUtil.UTF_8));
复制代码


这样,我们在 handler 中就不需要手动添加换行符了,如下所示:


   void channelRead(ChannelHandlerContext ctx, String msg) {       ch.write("Did you say '" + msg + "'?");   }
复制代码

不同平台的换行符

在 unix 和 windows 平台传递过文本文件的朋友可能会遇到一个问题,就是 windows 创建的文本文件,如果在 unix 下面打开的话,会发现每行后面多出了一个特殊字符,这是因为 unix 和 windows 平台定义的换行符是不同的。


在 unix 平台通常使用"\n"来换行,而在 windows 平台则使用""\r\n"来换行。


java 程序因为是跨平台的,写出的程序可能运行在 unix 平台,也可能运行在 windows 平台,所以我们需要有一个办法来获取平台的换行符,netty 提供了一个 LineSeparator 的类来完成这个工作。


LineSeparator 中有三个换行符的定义,分别是:


   public static final LineSeparator DEFAULT = new LineSeparator(StringUtil.NEWLINE);
public static final LineSeparator UNIX = new LineSeparator("\n");
public static final LineSeparator WINDOWS = new LineSeparator("\r\n");
复制代码


UNIX 和 WINDOWS 很好理解,他们就是我们刚刚讲到的不同的平台。


那么什么是 DEFAULT 呢?DEFAULT 中传入的 NEWLINE,实际上是从系统属性中获取到的,如果没有获取到,则使用默认的"\n"。


public static final String NEWLINE = SystemPropertyUtil.get("line.separator", "\n");
复制代码

字符串编码的实现

上面我们讲到了和字符串编码解码相关的类分别是 StringEncoder,LineEncoder 和 StringDecoder,我们来详细看下这三个类的实现。


首先是 StringEncoder,StringEncoder 继承了 MessageToMessageEncoder:


public class StringEncoder extends MessageToMessageEncoder<CharSequence> 
复制代码


泛型中的 CharSequence 表示 StringEncoder 要 encode 的对象是 CharSequence,也就是字符序列。


虽然大家常用 String 这个类,但是不一定大家都知道 String 其实是 CharSequence 的子类,所以 StringEncoder 也可以编码字符串。


StringEncoder 的编码逻辑很简单,将传入的字符串 msg 转换成为 CharBuffer,然后调用 ByteBufUtil 的 encodeString 方法就可以转换成为 ByteBuf,并加入 out 中去:


    protected void encode(ChannelHandlerContext ctx, CharSequence msg, List<Object> out) throws Exception {        if (msg.length() == 0) {            return;        }        out.add(ByteBufUtil.encodeString(ctx.alloc(), CharBuffer.wrap(msg), charset));    }
复制代码


LineEncoder 和 StringEncoder 很类似,它也是继承自 MessageToMessageEncoder:


public class LineEncoder extends MessageToMessageEncoder<CharSequence> 
复制代码


不同之处在于 encoder 方法:


    protected void encode(ChannelHandlerContext ctx, CharSequence msg, List<Object> out) throws Exception {        ByteBuf buffer = ByteBufUtil.encodeString(ctx.alloc(), CharBuffer.wrap(msg), charset, lineSeparator.length);        buffer.writeBytes(lineSeparator);        out.add(buffer);    }
复制代码


ByteBufUtil 的 encodeString 多了一个 lineSeparator.length 参数,用来预留 lineSeparator 的位置,然后在返回的 ByteBuf 后面加上 lineSeparator 作为最终的输出。


StringDecoder 是和 StringEncoder 相反的过程:


public class StringDecoder extends MessageToMessageDecoder<ByteBuf> 
复制代码


这里的 ByteBuf 表示的是要解码的对象是 ByteBuf,我们看下他的解码方法:


    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {        out.add(msg.toString(charset));    }
复制代码


直接调用 msg.toString 方法即可将 ByteBuf 转换成为字符串。

总结

以上就是 netty 中对字符串的编码解码器,通过使用这几个编码解码器可以大大简化我们的工作。


本文已收录于 http://www.flydean.com/14-6-netty-codec-string/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

发布于: 刚刚阅读数: 2
用户头像

关注公众号:程序那些事,更多精彩等着你! 2020.06.07 加入

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧,尽在公众号:程序那些事!

评论

发布
暂无评论
netty系列之:netty中常用的字符串编码解码器_Java_程序那些事_InfoQ写作社区