
public abstract class FrameDecoder extends SimpleChannelUpstreamHandler implements LifeCycleAwareChannelHandler
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;
}
}
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.
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 extendsFrameDecoder{ public FirstDecoder() { super(true); // Enable unfold }@Overrideprotected Object decode(ChannelHandlerContextctx,Channelchannel,ChannelBufferbuf) { ... // 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; } } }
ChannelHandler.Sharable| Modifier and Type | Field and Description |
|---|---|
protected ChannelBuffer |
cumulation |
static int |
DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS |
| Modifier | Constructor and Description |
|---|---|
protected |
FrameDecoder() |
protected |
FrameDecoder(boolean unfold) |
| Modifier and Type | Method and Description |
|---|---|
protected int |
actualReadableBytes()
Returns the actual number of readable bytes in the internal cumulative
buffer of this decoder.
|
void |
afterAdd(ChannelHandlerContext ctx) |
void |
afterRemove(ChannelHandlerContext ctx) |
protected ChannelBuffer |
appendToCumulation(ChannelBuffer input) |
void |
beforeAdd(ChannelHandlerContext ctx) |
void |
beforeRemove(ChannelHandlerContext ctx) |
void |
channelClosed(ChannelHandlerContext ctx,
ChannelStateEvent e)
Invoked when a
Channel was closed and all its related resources
were released. |
void |
channelDisconnected(ChannelHandlerContext ctx,
ChannelStateEvent e)
Invoked when a
Channel was disconnected from its remote peer. |
protected void |
cleanup(ChannelHandlerContext ctx,
ChannelStateEvent e)
|
protected abstract Object |
decode(ChannelHandlerContext ctx,
Channel channel,
ChannelBuffer buffer)
Decodes the received packets so far into a frame.
|
protected Object |
decodeLast(ChannelHandlerContext ctx,
Channel channel,
ChannelBuffer buffer)
Decodes the received data so far into a frame when the channel is
disconnected.
|
void |
exceptionCaught(ChannelHandlerContext ctx,
ExceptionEvent e)
Invoked when an exception was raised by an I/O thread or a
ChannelHandler. |
protected ChannelBuffer |
extractFrame(ChannelBuffer buffer,
int index,
int length)
Extract a Frame of the specified buffer.
|
int |
getMaxCumulationBufferCapacity()
See
setMaxCumulationBufferCapacity(int) for explaintation of this setting |
int |
getMaxCumulationBufferComponents()
Returns the maximum number of components in the cumulation buffer.
|
protected ChannelBuffer |
internalBuffer()
Returns the internal cumulative buffer of this decoder.
|
boolean |
isUnfold() |
void |
messageReceived(ChannelHandlerContext ctx,
MessageEvent e)
Invoked when a message object (e.g:
ChannelBuffer) was received
from a remote peer. |
protected ChannelBuffer |
newCumulationBuffer(ChannelHandlerContext ctx,
int minimumCapacity)
Create a new
ChannelBuffer which is used for the cumulation. |
void |
replace(String handlerName,
ChannelHandler handler)
|
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". |
void |
setMaxCumulationBufferComponents(int maxCumulationBufferComponents)
Sets the maximum number of components in the cumulation buffer.
|
void |
setUnfold(boolean unfold) |
protected void |
unfoldAndFireMessageReceived(ChannelHandlerContext context,
SocketAddress remoteAddress,
Object result) |
protected ChannelBuffer |
updateCumulation(ChannelHandlerContext ctx,
ChannelBuffer input) |
channelBound, channelConnected, channelInterestChanged, channelOpen, channelUnbound, childChannelClosed, childChannelOpen, handleUpstream, writeCompletepublic static final int DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS
protected ChannelBuffer cumulation
protected FrameDecoder()
protected FrameDecoder(boolean unfold)
public final boolean isUnfold()
public final void setUnfold(boolean unfold)
public final int getMaxCumulationBufferCapacity()
setMaxCumulationBufferCapacity(int) for explaintation of this settingpublic final void setMaxCumulationBufferCapacity(int copyThreshold)
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 usagecopyThreshold - the threshold (in bytes) or Integer.MAX_VALUE to
disable it. The value must be at least 0IllegalStateException - get thrown if someone tries to change this setting after the
Decoder was added to the ChannelPipelinepublic final int getMaxCumulationBufferComponents()
DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS.public final void setMaxCumulationBufferComponents(int maxCumulationBufferComponents)
DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS
and its minimum allowed value is 2.public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception
SimpleChannelUpstreamHandlerChannelBuffer) was received
from a remote peer.messageReceived in class SimpleChannelUpstreamHandlerExceptionprotected ChannelBuffer appendToCumulation(ChannelBuffer input)
protected ChannelBuffer updateCumulation(ChannelHandlerContext ctx, ChannelBuffer input)
public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception
SimpleChannelUpstreamHandlerChannel was disconnected from its remote peer.channelDisconnected in class SimpleChannelUpstreamHandlerExceptionpublic void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception
SimpleChannelUpstreamHandlerChannel was closed and all its related resources
were released.channelClosed in class SimpleChannelUpstreamHandlerExceptionpublic void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception
SimpleChannelUpstreamHandlerChannelHandler.exceptionCaught in class SimpleChannelUpstreamHandlerExceptionprotected abstract Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception
extractFrame(ChannelBuffer, int, int) method,
to make optimizations easier later.ctx - the context of this handlerchannel - the current channelbuffer - 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.null if there's not enough data in the buffer to decode a frame.Exceptionprotected Object decodeLast(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception
ctx - the context of this handlerchannel - the current channelbuffer - 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.null if there's not enough data in the buffer to decode a frame.Exceptionprotected final void unfoldAndFireMessageReceived(ChannelHandlerContext context, SocketAddress remoteAddress, Object result)
protected void cleanup(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception
channelDisconnected(ChannelHandlerContext, ChannelStateEvent) and
channelClosed(ChannelHandlerContext, ChannelStateEvent)Exceptionprotected ChannelBuffer newCumulationBuffer(ChannelHandlerContext ctx, int minimumCapacity)
ChannelBuffer which is used for the cumulation.
Sub-classes may override this.ctx - ChannelHandlerContext for this handlerChannelBuffer which is used for cumulationpublic void replace(String handlerName, ChannelHandler handler)
FrameDecoder in the ChannelPipeline with the given ChannelHandler. All
remaining bytes in the ChannelBuffer will get send to the new ChannelHandler that was used
as replacementprotected int actualReadableBytes()
internalBuffer().readableBytes().protected ChannelBuffer internalBuffer()
protected ChannelBuffer extractFrame(ChannelBuffer buffer, int index, int length)
public void beforeAdd(ChannelHandlerContext ctx) throws Exception
beforeAdd in interface LifeCycleAwareChannelHandlerExceptionpublic void afterAdd(ChannelHandlerContext ctx) throws Exception
afterAdd in interface LifeCycleAwareChannelHandlerExceptionpublic void beforeRemove(ChannelHandlerContext ctx) throws Exception
beforeRemove in interface LifeCycleAwareChannelHandlerExceptionpublic void afterRemove(ChannelHandlerContext ctx) throws Exception
afterRemove in interface LifeCycleAwareChannelHandlerExceptionCopyright © 2008-2014 The Netty Project. All Rights Reserved.