Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

FixedLengthFrameDecoder

FixedLengthFrameDecoder is very simple, directly through the constructor to set the size of the fixed length frameLength, no matter how much data the receiver at a time, will be decoded strictly according to frameLength. If a cumulative frameLength message is read, the decoder thinks it has got a complete message. If the message length is less than frameLength, the FixedLengthFrameDecoder decoder waits for subsequent packets to arrive until the complete message is obtained. Let’s take a look at an example of how easy it is to implement fixed-length decoding using Netty.

public class EchoServer { public void startEchoServer(int port) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) { ch.pipeline().addLast(new FixedLengthFrameDecoder(10)); ch.pipeline().addLast(new EchoServerHandler()); }}); ChannelFuture f = b.bind(port).sync(); f.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } public static void main(String[] args) throws Exception { new EchoServer().startEchoServer(8088); } } @Sharable public class EchoServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { System.out.println("Receive client : [" + ((ByteBuf) msg).toString(CharsetUtil.UTF_8) + "]"); }}Copy the code

A fixed 10-byte decoder is used in the server-side code above, and the result is printed through the EchoServerHandler after decoding. We can start the server, send data to the server through the Telnet command, and observe the output of the code.

Client input:

telnet localhost 8088

Trying ::1...

Connected to localhost.

Escape character is '^]'.

1234567890123

456789012
Copy the code

Server output:

Receive client : [1234567890]

Receive client : [123

45678]
Copy the code

Special delimiter decoder DelimiterBasedFrameDecoder

Use special delimiter decoder DelimiterBasedFrameDecoder before we need to understand the role of the following attributes.

  • delimiters

Delimiters specify a special delimiter, which is passed in by writing ByteBuf as an argument. Delimiters are of type ByteBuf array, so we can specify multiple delimiters at the same time, but will ultimately choose the one with the shortest length for message splitting.

For example, the data received by the receiver is:

+--------------+

| ABC\nDEF\r\n |

+--------------+
Copy the code

If you specify multiple separator for and \ n \ r \ n, DelimiterBasedFrameDecoder will degenerate into use LineBasedFrameDecoder parsing, so can decode the two messages.

+-----+-----+

| ABC | DEF |

+-----+-----+
Copy the code

If the specific delimiter specified is only \r\n, only one message will be decoded:

+----------+

| ABC\nDEF |

+----------+

Copy the code
  • maxLength

MaxLength indicates the limit of the maximum packet length. If maxLength is exceeded and the specified delimiter is not detected, a TooLongFrameException is thrown. You can say that maxLength is a measure of protection for programs in extreme cases.

  • failFast

FailFast and maxLength are used together. By setting failFast to control when a TooLongFrameException is thrown, Netty has considered all the details. If failFast=true, TooLongFrameException will be thrown immediately after maxLength is exceeded and decoding will not continue. If failFast=false, TooLongFrameException will not be thrown until a complete message is decoded.

  • stripDelimiter

StripDelimiter is used to determine whether the decoded message is delimited. If stripDelimiter=false, and the specific delimiter is \ N, the result decoded from the above packets is:

+-------+---------+

| ABC\n | DEF\r\n |

+-------+---------+
Copy the code

Below we still combined with DelimiterBasedFrameDecoder usage code sample, still on the basis of the fixed code encoder section used to do a little change, the introduction of special delimiter decoder DelimiterBasedFrameDecoder:

b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) { ByteBuf delimiter = Unpooled.copiedBuffer("&".getBytes()); ch.pipeline().addLast(new DelimiterBasedFrameDecoder(10, true, true, delimiter)); ch.pipeline().addLast(new EchoServerHandler()); }});Copy the code

We still use Telnet to simulate the client to send data, and observe the output result of the code. We can find that because maxLength is set to 10, it throws an exception when parsing the third message.

Client input:

telnet localhost 8088

Trying ::1...

Connected to localhost.

Escape character is '^]'.

hello&world&1234567890ab
Copy the code

Server output:

Receive client : [hello] Receive client : [world] on September 25, 2020 io.net ty 8:46:01 afternoon. Channel. DefaultChannelPipeline onUnhandledInboundException warning: An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception. io.netty.handler.codec.TooLongFrameException: frame length exceeds 10: 13 - discarded at io.netty.handler.codec.DelimiterBasedFrameDecoder.fail(DelimiterBasedFrameDecoder.java:302) at io.netty.handler.codec.DelimiterBasedFrameDecoder.decode(DelimiterBasedFrameDecoder.java:268) at io.netty.handler.codec.DelimiterBasedFrameDecoder.decode(DelimiterBasedFrameDecoder.java:218)Copy the code

The length field decoder LengthFieldBasedFrameDecoder

Decoder LengthFieldBasedFrameDecoder length field is to solve the problem of TCP unpacking/stick pack the most commonly used * * decoder. * * it can cover most basically based on the length of the unpacking, open source message middleware LengthFieldBasedFrameDecoder RocketMQ is used for decoding. Compared LengthFieldBasedFrameDecoder FixedLengthFrameDecoder are more complex and DelimiterBasedFrameDecoder, then we will study together under the powerful decoder.

First of all, we also know several important properties of the LengthFieldBasedFrameDecoder first, here I put them into two main parts: the length of the domain specific attribute decoder as well as other decoders, such as specific delimiters decoder similar attributes.

  • Length domain decoder specific properties.
Private final int lengthFieldOffset; Private final int lengthFieldLength; In many more complex protocol designs, the length field contains not only the length of the message, but also other data, such as version number, data type, data state, etc. LengthAdjustment = lengthAdjustment = lengthAdjustment; lengthAdjustment = lengthAdjustment = lengthAdjustment; lengthAdjustment = lengthAdjustment = lengthAdjustment; / / need to skip the initial bytes after decoding, which is the starting point of the message content field private final int initialBytesToStrip; LengthFieldEndOffset = lengthFieldOffset + lengthFieldLength private Final int lengthFieldEndOffset;Copy the code
  • Properties similar to fixed length decoders and specific separator decoders.
private final int maxFrameLength; // Maximum packet length private Final Boolean failFast; // Whether to throw TooLongFrameException immediately, with maxFrameLength using private Boolean discardingTooLongFrame; // Whether in discard mode private long tooLongFrameLength; // Indicates the number of bytes to be discarded. Private long bytestdiscard; // Total number of bytes discardedCopy the code