This is the 19th day of my participation in the August Wenwen Challenge.More challenges in August

preface

In August, I will update an article every day and take everything seriously. Come on

In previous articles, we discussed decoders and encoders separately, with links below.

Netty source code analysis series (12) Netty decoder

Netty source code analysis series (13) Netty encoder

In fact, Netty also provides a third way for encoding and decoding, that is the codec. A codec, as its name suggests, is a program that combines encoding and decoding capabilities.

Codecs can place both inbound and outbound data and information transformations in the same class, making them more practical for some scenarios.

Overview of codecs

Netty provides abstract codec classes that combine pairs of codecs and encoders to provide identical operations on bytes and messages. These classes implement the ChannelOutboundHandler and ChannelInboundHandler interfaces.

Netty’s abstract codec classes are of the following two types:

  • Implement ByteToMessageCodec (ByteToMessageCodec).

  • Implement from MessageToMessageCodec (MessageToMessageCodec).

ByteToMessageCodec abstract class

The ByteToMessageCodec abstract class is used to encode/decode bytes in real time into a message codec, which can be thought of as a combination of ByteToMessageDecoder and MessageToByteEncoder.

Note that subclasses of ByteToMessageCodec must never be annotated with @sharable.

The core source code for the ByteToMessageCodec abstract class is as follows:

public abstract class ByteToMessageCodec<I> extends ChannelDuplexHandler {

    private final TypeParameterMatcher outboundMsgMatcher;
    private final MessageToByteEncoder<I> encoder;

    private final ByteToMessageDecoder decoder = new ByteToMessageDecoder() {
        @Override
        public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
            ByteToMessageCodec.this.decode(ctx, in, out);
        }

        @Override
        protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
            ByteToMessageCodec.this.decodeLast(ctx, in, out); }};protected ByteToMessageCodec(a) {
        this(true);
    }

   
    protected ByteToMessageCodec(Class<? extends I> outboundMessageType) {
        this(outboundMessageType, true);
    }

  
    protected ByteToMessageCodec(boolean preferDirect) {
        ensureNotSharable();
        outboundMsgMatcher = TypeParameterMatcher.find(this, ByteToMessageCodec.class, "I");
        encoder = new Encoder(preferDirect);
    }

   
    protected ByteToMessageCodec(Class<? extends I> outboundMessageType, boolean preferDirect) {
        ensureNotSharable();
        outboundMsgMatcher = TypeParameterMatcher.get(outboundMessageType);
        encoder = new Encoder(preferDirect);
    }

   
    public boolean acceptOutboundMessage(Object msg) throws Exception {
        return outboundMsgMatcher.match(msg);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        decoder.channelRead(ctx, msg);
    }

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        encoder.write(ctx, msg, promise);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        decoder.channelReadComplete(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        decoder.channelInactive(ctx);
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        try {
            decoder.handlerAdded(ctx);
        } finally{ encoder.handlerAdded(ctx); }}@Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        try {
            decoder.handlerRemoved(ctx);
        } finally{ encoder.handlerRemoved(ctx); }}protected abstract void encode(ChannelHandlerContext ctx, I msg, ByteBuf out) throws Exception;

    protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception;

    protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        if(in.isReadable()) { decode(ctx, in, out); }}private final class Encoder extends MessageToByteEncoder<I> {
        Encoder(boolean preferDirect) {
            super(preferDirect);
        }

        @Override
        public boolean acceptOutboundMessage(Object msg) throws Exception {
            return ByteToMessageCodec.this.acceptOutboundMessage(msg);
        }

        @Override
        protected void encode(ChannelHandlerContext ctx, I msg, ByteBuf out) throws Exception {
            ByteToMessageCodec.this.encode(ctx, msg, out); }}}Copy the code

In the above code, focus on the following methods:

  • decode(): This is the abstract method that must be implemented to convert the inbound ByteBuf to the specified message format and forward it to the next one in the pipeChannelInboundHandler.
  • encode()This method is an abstract method that developers need to implement. This method will be called for each message encoded and written to the outbound ByteBuf.
  • decodeLast()The default implementation provided by Netty is simply calleddecode()Methods. whenChannelThis method will be called once when the state of the. This method can be overridden to provide special handling.

MessageToMessageCodec abstract class

The MessageToMessageCodec abstract class is used to encode/decode messages in real time as codecs for messages. It can be considered a combination of MessageToMessageDecoder and MessageToMessageEncoder ‘.

MessageToMessageCodec abstract class core source code is as follows:

public abstract class MessageToMessageCodec<INBOUND_IN.OUTBOUND_IN> extends ChannelDuplexHandler {

    private final MessageToMessageEncoder<Object> encoder = new MessageToMessageEncoder<Object>() {

        @Override
        public boolean acceptOutboundMessage(Object msg) throws Exception {
            return MessageToMessageCodec.this.acceptOutboundMessage(msg);
        }

        @Override
        @SuppressWarnings("unchecked")
        protected void encode(ChannelHandlerContext ctx, Object msg, List<Object> out) throws Exception {
            MessageToMessageCodec.this.encode(ctx, (OUTBOUND_IN) msg, out); }};private final MessageToMessageDecoder<Object> decoder = new MessageToMessageDecoder<Object>() {

        @Override
        public boolean acceptInboundMessage(Object msg) throws Exception {
            return MessageToMessageCodec.this.acceptInboundMessage(msg);
        }

        @Override
        @SuppressWarnings("unchecked")
        protected void decode(ChannelHandlerContext ctx, Object msg, List<Object> out) throws Exception {
            MessageToMessageCodec.this.decode(ctx, (INBOUND_IN) msg, out); }};private final TypeParameterMatcher inboundMsgMatcher;
    private final TypeParameterMatcher outboundMsgMatcher;

    protected MessageToMessageCodec(a) {
        inboundMsgMatcher = TypeParameterMatcher.find(this, MessageToMessageCodec.class, "INBOUND_IN");
        outboundMsgMatcher = TypeParameterMatcher.find(this, MessageToMessageCodec.class, "OUTBOUND_IN");
    }

 
    protected MessageToMessageCodec( Class
        inboundMessageType, Class
        outboundMessageType) {
        inboundMsgMatcher = TypeParameterMatcher.get(inboundMessageType);
        outboundMsgMatcher = TypeParameterMatcher.get(outboundMessageType);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        decoder.channelRead(ctx, msg);
    }

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        encoder.write(ctx, msg, promise);
    }

    public boolean acceptInboundMessage(Object msg) throws Exception {
        return inboundMsgMatcher.match(msg);
    }

    public boolean acceptOutboundMessage(Object msg) throws Exception {
        return outboundMsgMatcher.match(msg);
    }

    protected abstract void encode(ChannelHandlerContext ctx, OUTBOUND_IN msg, List<Object> out)
            throws Exception;

    protected abstract void decode(ChannelHandlerContext ctx, INBOUND_IN msg, List<Object> out)
            throws Exception;
}
Copy the code

In the above code, focus on the following methods:

  • Decode () : This is the abstract method that must be implemented to decode inbound messages (of type INBOUND_IN) into messages that are forwarded to the next ChannelInboundHandler in the ChannelPipeline.

  • Encode () : This method is an abstract method that developers need to implement. The outbound message (of type OUTBOUND_IN) is encoded as a message and then forwarded to the next ChannelOutboundHandler in the ChannelPipeline.

Note that if the message is of type ReferenceCounted, you need to call Referencecounde.ratain () on the message you just passed. This call is necessary because MessageToMessageCodec will call Referencecounde.ralease () on the encoded/decoded message.

Here is an example of a MessageToMessageCodec decoding an Integer to a Long, and then encoding a Long to an Integer.

public class NumberCodec extends MessageToMessageCodec<Integer.Long> {

    @Override
    protected void decode(ChannelHandlerContext ctx, Integer msg, List<Object> out) throws Exception {
            out.add(msg.longValue());
    }

    @Override
    protected void encode(ChannelHandlerContext ctx, Long msg, List<Object> out) throws Exception {
            out.add(msg.intValue())
    }
}
Copy the code

ChannelDuplexHandler class

ByteToMessageCodec and MessageToMessageCodec inherit from the ChannelDuplexHandler class. The ChannelDuplexHandler class is an implementation of ChannelHandler and represents a combination of ChannelInboundHandler and ChannelOutboundHandler. If ChannelHandler’s implementation needs to intercept operations and status updates, this ChannelDuplexHandler class would be a good place to start.

public class ChannelDuplexHandler extends ChannelInboundHandlerAdapter implements ChannelOutboundHandler {... }Copy the code

CombinedChannelDuplexHandler class

CombinedChannelDuplexHandler class is a subclass of ChannelDuplexHandler class, Use to combine ChannelInboundHandler and ChannelOutboundHandler into a single ChannelHandler.

In the previous article examples, the encoder and decoder are implemented separately. In fixed on the basis of the existing code, you can use CombinedChannelDuplexHandler class easily implement a codec, the only need to do is through CombinedChannelDuplexHandler class to combination of decoder and encoder.

For example, there is a decoder ByteToCharDecoder with the following code:

public class ByteToCharDecoder extends ByteToMessageDecoder {

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        if(in.readableBytes() >= 2){ out.add(in.readChar()); }}}Copy the code

ByteToCharDecoder is used to extract 2-byte characters from ByteBuf and write them to the List as char, which will be automatically boxed as Character objects.

The code of the encoder CharToByteEncoder is as follows:

public class CharToByteEncoder extends MessageToByteEncoder<Character> {

    @Override
    protected void encode(ChannelHandlerContext ctx, Character msg, ByteBuf out) throws Exception { out.writeChar(msg); }}Copy the code

CharToByteEncoder encodes char messages to ByteBuf.

Now that you have an encoder and a decoder, you need to combine them into a codec. CombinedByteCharCodec code is as follows:

public class CombinedByteCharCodec extends CombinedChannelDuplexHandler<ByteToCharDecoder.CharToByteEncoder> {

    public CombinedByteCharCodec(a){
        super(new ByteToCharDecoder(),newCharToByteEncoder()); }}Copy the code

The parameters of CombinedByteCharCodec are the decoder and encoder, which are combined by the constructor function of the parent class. Combining encoders and decoders in the above way makes the program simpler and more flexible, and avoids writing multiple codec classes.

Of course, whether or not to use CombinedByteCharCodec depends on the project style.

conclusion

Through the above analysis of the codec, I believe that partners for encoders, decoders and codecs have some understanding, the next section we will implement a custom codec.

At the end

I am a coder who is being beaten and still trying to move on. If this article is helpful to you, remember to like and follow yo, thanks!