SoFunction
Updated on 2025-04-11

How to customize the codec

Netty Custom Encoding Decoder

Enter the stack:InboundHandler, out of the stack:OutboundHandler. Netty buildchainto handle business.

Customize a decoder

The decoder mainly decodes the messages sent by the client, so it is aStacked processor, so it will have a stack entry markibound=true;

Generally, we will implement our own decoding logic by the base-level Netty ->This is the decoding abstract class. By default, we want to implement an abstract method

#decode 

This method has about three parameters;

  • ChannelHandlerContext channelHandlerContextThis is context information, can be obtainedaislepipelineetc.
  • ByteBuf byteBufThe message sent by the client is that the object with this parameter is what we want to pass through.read***The method reads the data type we need, which can beLongByteThen we can convert data of the same type into the format we need.
  • List<Object> listCollection, passing the decoded data to the nextinboundHandlerProcessing class.

Code Example

@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List&lt;Object&gt; list) {
    // Long =&gt; 8 byte
    if (() &gt;= 8) {
        (());
    } else ("Byte number exception =&gt; {}", ());
}

This way we implement a simple decoder.

Customize an encoder

The decoder mainly encodes the messages to be sent to the client by the server, so it is aStacked processor, so it will have a stack entry markoutbound=true;

Using the abstract class provided by Netty =><T>Generic T represents the type of message you want to send, implementing abstract method =>#encodeThere are three parameters of the method:

  • ChannelHandlerContext channelHandlerContextThis is context information, can be obtainedaislepipelineetc.
  • Long msgMessages to be sent to the client on the server
  • ByteBuf byteBuf

Code Example

@Override
protected void encode(ChannelHandlerContext channelHandlerContext, Long msg, ByteBuf byteBuf) {
    (msg);
    ("Send message successfully");
}

Follow-up ->

ByteToMessageIn fact, you will encounter some problems during use, such as:

When our decoder wants to convert bytes into oneInteger, we knowIntegerIt is four bytes, but if there are not enough four bytes when reading, we need to do some judgment logic =>if (() >= 4)When the return value istrueWhen I can continue to execute the decoding logic.

So how can we skip this step and directly carry out our transformation logic without judging?

You can use Netty's

The principle of not judging the number of readable bytes:

ReplayingDecoderyesByteToMessageThe subclass of the source code is as follows:

public abstract class ReplayingDecoder<S> extends ByteToMessageDecoder {
    ...
}

ReplayingDecoderThe secret is rightByteToMessageofCallDecode(...)Rewriting the method, let’s take a look at the specific implementation:

@Override
protected void callDecode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
    (in);
    try {
        while (()) {
            int oldReaderIndex = checkpoint = ();
            int outSize = ();

            if (outSize > 0) {
                fireChannelRead(ctx, out, outSize);
                ();

                // Check if this handler was removed before continuing with decoding.
                // If it was removed, it is not safe to continue to operate on the buffer.
                //
                // See:
                // - /netty/netty/issues/4635
                if (()) {
                    break;
                }
                outSize = 0;
            }

            S oldState = state;
            int oldInputLength = ();
            try {
                decodeRemovalReentryProtection(ctx, replayable, out);

                // Check if this handler was removed before continuing the loop.
                // If it was removed, it is not safe to continue to operate on the buffer.
                //
                // See /netty/netty/issues/1664
                if (()) {
                    break;
                }

                if (outSize == ()) {
                    if (oldInputLength == () && oldState == state) {
                        throw new DecoderException(
                                (getClass()) + ".decode() must consume the inbound " +
                                "data or change its state if it did not decode anything.");
                    } else {
                        // Previous data has been discarded or caused state transition.
                        // Probably it is reading on.
                        continue;
                    }
                }
            } catch (Signal replay) {
                (REPLAY);

                // Check if this handler was removed before continuing the loop.
                // If it was removed, it is not safe to continue to operate on the buffer.
                //
                // See /netty/netty/issues/1664
                if (()) {
                    break;
                }

                // Return to the checkpoint (or oldPosition) and retry.
                int checkpoint = ;
                if (checkpoint >= 0) {
                    (checkpoint);
                } else {
                    // Called by cleanup() - no need to maintain the readerIndex
                    // anymore because the buffer has been released already.
                }
                break;
            }

            if (oldReaderIndex == () && oldState == state) {
                throw new DecoderException(
                       (getClass()) + ".decode() method must consume the inbound data " +
                       "or change its state if it decoded something.");
            }
            if (isSingleDecode()) {
                break;
            }
        }
    } catch (DecoderException e) {
        throw e;
    } catch (Exception cause) {
        throw new DecoderException(cause);
    }
}

Implementing logic that does not require judgment is because

int oldReaderIndex = checkpoint = (); 

If an exception occurs during execution, it will be reset in the codeindex

Summarize

AlthoughReplayingDecoderIt saves the logic of judgment, but from the implementation logic of his code, it is seen that it is constantly retrying by throwing exceptions, so in some special cases it will cause performance degradation. So when choosing again, you should judge whether to use it according to your actual needsByteToMessageOr useReplayingDecoder

The above is personal experience. I hope you can give you a reference and I hope you can support me more.