Class FrameDecoder

  • All Implemented Interfaces:
    ChannelHandler, ChannelUpstreamHandler, LifeCycleAwareChannelHandler
    Direct Known Subclasses:
    BigIntegerDecoder, DelimiterBasedFrameDecoder, FixedLengthFrameDecoder, LengthFieldBasedFrameDecoder, LineBasedFrameDecoder, PortUnificationServerHandler, ProtobufVarint32FrameDecoder, ReplayingDecoder, SpdyFrameCodec, SslHandler

    public abstract class FrameDecoder
    extends SimpleChannelUpstreamHandler
    implements LifeCycleAwareChannelHandler
    Decodes the received ChannelBuffers into a meaningful frame object.

    In a stream-based transport such as TCP/IP, packets can be fragmented and reassembled during transmission even in a LAN environment. For example, let us assume you have received three packets:

     +-----+-----+-----+
     | ABC | DEF | GHI |
     +-----+-----+-----+
     
    because of the packet fragmentation, a server can receive them like the following:
     +----+-------+---+---+
     | AB | CDEFG | H | I |
     +----+-------+---+---+
     

    FrameDecoder helps you defrag the received packets into one or more meaningful frames that could be easily understood by the application logic. In case of the example above, your FrameDecoder implementation could defrag the received packets like the following:

     +-----+-----+-----+
     | ABC | DEF | GHI |
     +-----+-----+-----+
     

    The following code shows an example handler which decodes a frame whose first 4 bytes header represents the length of the frame, excluding the header.

     MESSAGE FORMAT
     ==============
    
     Offset:  0        4                   (Length + 4)
              +--------+------------------------+
     Fields:  | Length | Actual message content |
              +--------+------------------------+
    
     DECODER IMPLEMENTATION
     ======================
    
     public class IntegerHeaderFrameDecoder extends FrameDecoder {
    
       @Override
       protected Object decode(ChannelHandlerContext ctx,
                               channel,
                               ChannelBuffer buf) throws Exception {
    
         // Make sure if the length field was received.
         if (buf.readableBytes() < 4) {
            // The length field was not received yet - return null.
            // This method will be invoked again when more packets are
            // received and appended to the buffer.
            return null;
         }
    
         // The length field is in the buffer.
    
         // Mark the current buffer position before reading the length field
         // because the whole frame might not be in the buffer yet.
         // We will reset the buffer position to the marked position if
         // there's not enough bytes in the buffer.
         buf.markReaderIndex();
    
         // Read the length field.
         int length = buf.readInt();
    
         // Make sure if there's enough bytes in the buffer.
         if (buf.readableBytes() < length) {
            // The whole bytes were not received yet - return null.
            // This method will be invoked again when more packets are
            // received and appended to the buffer.
    
            // Reset to the marked position to read the length field again
            // next time.
            buf.resetReaderIndex();
    
            return null;
         }
    
         // There's enough bytes in the buffer. Read it.
         ChannelBuffer frame = buf.readBytes(length);
    
         // Successfully decoded a frame.  Return the decoded frame.
         return frame;
       }
     }
     

    Returning a POJO rather than a ChannelBuffer

    Please note that you can return an object of a different type than ChannelBuffer in your decode() and decodeLast() implementation. For example, you could return a POJO so that the next ChannelUpstreamHandler receives a MessageEvent which contains a POJO rather than a ChannelBuffer.

    Replacing a decoder with another decoder in a pipeline

    If you are going to write a protocol multiplexer, you will probably want to replace a FrameDecoder (protocol detector) with another FrameDecoder or ReplayingDecoder (actual protocol decoder). It is not possible to achieve this simply by calling ChannelPipeline.replace(ChannelHandler, String, ChannelHandler), but some additional steps are required:

     public class FirstDecoder extends FrameDecoder {
    
         public FirstDecoder() {
             super(true); // Enable unfold
         }
    
         @Override
         protected Object decode(ChannelHandlerContext ctx,
                                 Channel channel,
                                 ChannelBuffer buf) {
             ...
             // Decode the first message
             Object firstMessage = ...;
    
             // Add the second decoder
             ctx.getPipeline().addLast("second", new SecondDecoder());
    
             // Remove the first decoder (me)
             ctx.getPipeline().remove(this);
    
             if (buf.readable()) {
                 // Hand off the remaining data to the second decoder
                 return new Object[] { firstMessage, buf.readBytes(buf.readableBytes()) };
             } else {
                 // Nothing to hand off
                 return firstMessage;
             }
         }
     }
     
    • Field Detail

      • DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS

        public static final int DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS
        See Also:
        Constant Field Values
      • unfold

        private boolean unfold
      • copyThreshold

        private int copyThreshold
      • maxCumulationBufferComponents

        private int maxCumulationBufferComponents
    • Constructor Detail

      • FrameDecoder

        protected FrameDecoder()
      • FrameDecoder

        protected FrameDecoder​(boolean unfold)
    • Method Detail

      • isUnfold

        public final boolean isUnfold()
      • setUnfold

        public final void setUnfold​(boolean unfold)
      • setMaxCumulationBufferCapacity

        public final void setMaxCumulationBufferCapacity​(int copyThreshold)
        Set the maximal capacity of the internal cumulation ChannelBuffer to use before the FrameDecoder tries to minimize the memory usage by "byte copy". What you use here really depends on your application and need. Using Integer.MAX_VALUE will disable all byte copies but give you the cost of a higher memory usage if big ChannelBuffer's will be received. By default a threshold of 0 is used, which means it will always copy to try to reduce memory usage
        Parameters:
        copyThreshold - the threshold (in bytes) or Integer.MAX_VALUE to disable it. The value must be at least 0
        Throws:
        java.lang.IllegalStateException - get thrown if someone tries to change this setting after the Decoder was added to the ChannelPipeline
      • getMaxCumulationBufferComponents

        public final int getMaxCumulationBufferComponents()
        Returns the maximum number of components in the cumulation buffer. If the number of the components in the cumulation buffer exceeds this value, the components of the cumulation buffer are consolidated into a single component, involving memory copies. The default value of this property DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS.
      • setMaxCumulationBufferComponents

        public final void setMaxCumulationBufferComponents​(int maxCumulationBufferComponents)
        Sets the maximum number of components in the cumulation buffer. If the number of the components in the cumulation buffer exceeds this value, the components of the cumulation buffer are consolidated into a single component, involving memory copies. The default value of this property is DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS and its minimum allowed value is 2.
      • decode

        protected abstract java.lang.Object decode​(ChannelHandlerContext ctx,
                                                   Channel channel,
                                                   ChannelBuffer buffer)
                                            throws java.lang.Exception
        Decodes the received packets so far into a frame. If an sub-class wants to extract a frame out of the buffer it should use the extractFrame(ChannelBuffer, int, int) method, to make optimizations easier later.
        Parameters:
        ctx - the context of this handler
        channel - the current channel
        buffer - the cumulative buffer of received packets so far. Note that the buffer might be empty, which means you should not make an assumption that the buffer contains at least one byte in your decoder implementation.
        Returns:
        the decoded frame if a full frame was received and decoded. null if there's not enough data in the buffer to decode a frame.
        Throws:
        java.lang.Exception
      • decodeLast

        protected java.lang.Object decodeLast​(ChannelHandlerContext ctx,
                                              Channel channel,
                                              ChannelBuffer buffer)
                                       throws java.lang.Exception
        Decodes the received data so far into a frame when the channel is disconnected.
        Parameters:
        ctx - the context of this handler
        channel - the current channel
        buffer - the cumulative buffer of received packets so far. Note that the buffer might be empty, which means you should not make an assumption that the buffer contains at least one byte in your decoder implementation.
        Returns:
        the decoded frame if a full frame was received and decoded. null if there's not enough data in the buffer to decode a frame.
        Throws:
        java.lang.Exception
      • callDecode

        private void callDecode​(ChannelHandlerContext context,
                                Channel channel,
                                ChannelBuffer cumulation,
                                java.net.SocketAddress remoteAddress)
                         throws java.lang.Exception
        Throws:
        java.lang.Exception
      • unfoldAndFireMessageReceived

        protected final void unfoldAndFireMessageReceived​(ChannelHandlerContext context,
                                                          java.net.SocketAddress remoteAddress,
                                                          java.lang.Object result)
      • actualReadableBytes

        protected int actualReadableBytes()
        Returns the actual number of readable bytes in the internal cumulative buffer of this decoder. You usually do not need to rely on this value to write a decoder. Use it only when you muse use it at your own risk. This method is a shortcut to internalBuffer().readableBytes().
      • internalBuffer

        protected ChannelBuffer internalBuffer()
        Returns the internal cumulative buffer of this decoder. You usually do not need to access the internal buffer directly to write a decoder. Use it only when you must use it at your own risk.
      • extractFrame

        protected ChannelBuffer extractFrame​(ChannelBuffer buffer,
                                             int index,
                                             int length)
        Extract a Frame of the specified buffer. By default this implementation will return a extract the sub-region of the buffer and create a new one. If an sub-class want to extract a frame from the buffer it should use this method by default. Be sure that this method MUST not modify the readerIndex of the given buffer