Netty series: string codecs commonly used in netty

Get into the habit of writing together! This is the 14th day of my participation in the "Nuggets Daily New Plan·April Update Challenge", click to view the details of the event .

Introduction

String is the most commonly used message format in our program, and it is also the simplest message format, but because the string is too simple to add more information, we choose to use byteBuf as the bottom layer in netty. Messaging carrier.

Although ByteBuf is used at the bottom, for programmers, they still want to be able to use this simplest string format, so is there any easy way?

String codec in netty

In order to solve the problem of passing strings in netty's channel, netty provides encoding and decoding for strings, namely StringEncoder and StringDecoder.

Let's see how they are used in the program, the first is to add StringDecoder and StringEncoder to 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));
复制代码

Note that here we also call LineBasedFrameDecoder before using StringDecoder, first divide the data by line, and then read the string.

So someone wants to ask, the decoder has added LineBasedFrameDecoder preprocessing, why is there no line separator added when writing?

In fact, there are two ways to deal with it. The first is to manually add line separators when writing strings to the channel, as shown below:

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

If you don't want to add a newline after msg every time, you can replace StringEncoder with LineEncoder, and the above pipeline becomes as follows:

   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));
复制代码

This way, we don't need to manually add newlines in the handler, like this:

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

Line breaks for different platforms

Friends who have passed text files on unix and windows platforms may encounter a problem, that is, the text files created by windows, if they are opened under unix, you will find that there is an extra special character after each line, this is because unix and windows Platform-defined line breaks are different.

在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);
    }
复制代码

The encodeString of ByteBufUtil has an additional lineSeparator.length parameter, which is used to reserve the position of lineSeparator, and then adds lineSeparator to the returned ByteBuf as the final output.

StringDecoder is the inverse of StringEncoder:

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

The ByteBuf here means that the object to be decoded is ByteBuf. Let's take a look at his decoding method:

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

The ByteBuf can be converted to a string by directly calling the msg.toString method.

Summarize

The above is the codec for strings in netty. By using these codecs, our work can be greatly simplified.

This article has been published on www.flydean.com/14-6-netty-…

The most popular interpretation, the most profound dry goods, the most concise tutorials, and many tricks you don't know are waiting for you to discover!

Welcome to pay attention to my official account: "Program those things", understand technology, understand you better!

Guess you like

Origin juejin.im/post/7086355900620865572