A codec is made up of two parts:
Decoder
Encoder
This should make it clear that the decoder is for inbound and the encoder is for outbound data.
Netty provides a rich set of abstract base classes that help you easily write decoders. These are divided into different types:
Decoders that decode from bytes to message(把字节解码为消息)
Decoders that decode from message to message(把消息解码为另一种格式的消息)
Decoders that decode from message to bytes(把消息解码为字节)
Lets define what the decoders responsibility(责任) is. A decoder is responsible for decoding inbound data from one format to another one. Because a decoder handles inbound data, its an abstract implementation of ChannelInboundHandler.
ByteToMessageDecoder中抽象方法decode()
/** * Decode the from one {@link ByteBuf} to an other. This method will be called till either the input * {@link ByteBuf} has nothing to read anymore, till nothing was read from the input {@link ByteBuf} or till * this method returns {@code null}. * * @param ctx the {@link ChannelHandlerContext} which this {@link ByteToMessageDecoder} belongs to * @param in the {@link ByteBuf} from which to read data * @param out the {@link List} to which decoded messages should be added * @throws Exception is thrown if an error accour */ protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception;
Often you want to decode from bytes to messages or even from bytes to another sequence of bytes. This is such a common task that Netty ship an abstract base class that you can use to do this. Exactly for this use case ByteToMessageDecoderis provided. Its an abstract base class that lets you write decoders that decode bytes into objects (POJOs) in an easy fashion.
Methods of ByteToMessageDecoder
decode() :The decode() method is the only abstract method you need to implement. Its called with a ByteBuf that holds all the received bytes and a List into which decoded messages should be added. The decode() method is called as long as it decodes something.
decodeLast() :Default implementation delegates to decode(). This method is called once, which is when the Channel goes inactive. If you need special handling here you may override decodeLast() to implement it.
Lets imagine we have a stream of bytes written from a remote peer to us, and that it contains simple integers. We want to handle each integer separately later in the ChannelPipeline, so we want to read the integers from the inbound ByteBuf and pass each integer separately(分别的) to the next ChannelInboundHandler in the ChannelPipeline.
You see in figure 7.2 that it will read bytes from the inbound ByteBuf of the ToIntegerDecoder, decode them, and write the decoded messages (int this case Integer) to the next ChannelInboundHandler in the ChannelPipeline. The figure also shows each integer will take up four bytes in the ByteBuf.
package codec; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageDecoder; import java.util.List; /** * 把字节转换为int * 继承抽象类ByteToMessageDecoder实现解码器 */ public class ToIntegerDecoder extends ByteToMessageDecoder { @Override public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { if (in.readableBytes() >= 4) { // Check if there are at least 4 bytes readable out.add(in.readInt()); //Read integer from inbound ByteBuf, add to the List of decodec messages } } }
MessageToMessageDecoder中抽象方法decode()
/** * Decode from one message to an other. This method will be called for each written message that can be handled * by this encoder. * * @param ctx the {@link ChannelHandlerContext} which this {@link MessageToMessageDecoder} belongs to * @param msg the message to decode to an other one * @param out the {@link List} to which decoded messages should be added * @throws Exception is thrown if an error accour */ protected abstract void decode(ChannelHandlerContext ctx, I msg, List<Object> out) throws Exception;
Methods of MessageToMessageDecoder
decode() :The decode()method is the only abstract method you need to implement. Its called for each inbound message of the decoder and lets you decode the message in an other message format . The decoded messages are then passed t o the next ChannelInboundHandlerin the ChannelPipeline.
decodeLast() :Default implementation delegates to decode(). decodeLast()is only called one time, which is when the Channelgoes inactive. If you need special handling here you may override decodeLast()to implement it.
If you want to decode a message to another type of message,MessageToMessageDecoderis the easiest way to go. The semantic(语义) is quite the same as for all the other decoders I explained before.
To illustrate(说明) some uses let me give you an example. Imagine you have integers and need to convert them to a string. This should be done as part of the ChannelPipeline and implemented as a separate decoder to make it as flexible(灵活) and reusable(可复用的) as possible.
Figure 7.3 shows the actual logic of the class I want to implement.
As it operates on messages and not bytes (in fact, a message can also be of type bytes). The inbound message is directly passed in the decode() method and decoded messages will be added to the List of decoded messages. So the decoder will receive inbound messages, decode them, and add them to the List of decoded messages. Once done it will forward all decoded messages to the next ChannelInboundHandlerin the ChannelPipeline.
该解码器的示例在我的demo中没有成功使用。
看我的demo在http://my.oschina.net/xinxingegeya/blog/282987
package codec; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageDecoder; import java.util.List; public class IntegerToStringDecoder extends MessageToMessageDecoder<Integer> { @Override public void decode(ChannelHandlerContext ctx, Integer msg, List<Object> out) throws Exception { out.add(String.valueOf(msg)); } }
As a counterpart to the decoders Netty offers, base classes help you to write encoders in an easy way. Again, these are divided into different types:
Encoders that encode from message to message(把消息编码为消息)
Encoders that encode from message to bytes(把消息编码为字节)
Before going deeper, lets define the responsibility of an encoder. An encoder is responsible for encoding outbound data from one format to another. As an encoder handles outbound data, it implements ChanneOutboundHandler.
MessageToByteEncoder中抽象方法encode()
/** * Encode a message into a {@link ByteBuf}. This method will be called for each written message that can be handled * by this encoder. * * @param ctx the {@link ChannelHandlerContext} which this {@link MessageToByteEncoder} belongs to * @param msg the message to encode * @param out the {@link ByteBuf} into which the encoded message will be written * @throws Exception is thrown if an error accour */ protected abstract void encode(ChannelHandlerContext ctx, I msg, ByteBuf out) throws Exception;
MessageToByteEncoder is provided to serve as an abstract base class for your encoder implementations that need to transform messages back into bytes.
shows the exact method you need to implement for your encoder, which is named encode.
Methods of MessageToByteEncoder
encode() :The encode()method is the only abstract method you need to implement. Its called with the outbound message, which was received by this encoder and encodes it in a ByteBuf. The ByteBufis then forwarded to the next ChannelOutboundHandlerin the ChannelPipeline.
Lets see it in action to better understand its usage. Ive written single short values and want to encode them into a ByteBuf to finally send them over the wire. IntegerToByteEncoder is the implementation thats used for this purpose.
Figure 7.5 shows the logic.
Figure 7.5 shows that it will receive short messages, encode, and write to a ByteBuf. This ByteBuf is then forwarded to the next ChannelOutboundHandler in the ChannelPipeline. As you can also see in figure 7.5, every short will take up 2 bytes in the ByteBuf.
package codec; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; public class IntegerToByteEncoder extends MessageToByteEncoder<Short> { @Override public void encode(ChannelHandlerContext ctx, Short msg, ByteBuf out) throws Exception { out.writeShort(msg); } }
MessageToMessageEncoder中抽象方法encode()
/** * Encode from one message to an other. This method will be called for each written message that can be handled * by this encoder. * * @param ctx the {@link ChannelHandlerContext} which this {@link MessageToMessageEncoder} belongs to * @param msg the message to encode to an other one * @param out the {@link List} into which the encoded msg should be added * needs to do some kind of aggragation * @throws Exception is thrown if an error accour */ protected abstract void encode(ChannelHandlerContext ctx, I msg, List<Object> out) throws Exception;
Suppose you need a way to encode from one message to another, similar to what you did for inbound data with MessageToMessageDecoder. MessageToMessageEncoder fills this gap.
Shows the method you need to implement for your encoder, which is named encode.
Methods of MessageToMessageEncoder
encode() :The encode()method is the only abstract method you need to implement. Its called for each message written with write() and encode the message to one or multiple new messages. The encoded messages are then forwarded to the next ChannelOutboundHandler in the ChannelPipeline.
Again let us look at some example. Imaging you need to encode Integer messages to String messages you could do this easily with MessageToMessageEncoder.
Figure 7.6 shows the logic.
The encoder encode Integer messages and forward them to the next ChannelOutboundHandlerin the ChannelPipeline.
下面这个编码器在一个demo中我试过,demo没运行成功。
看这篇文章http://my.oschina.net/xinxingegeya/blog/282987。原来使用
package codec; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageEncoder; import java.util.List; public class IntegerToStringEncoder extends MessageToMessageEncoder<Integer> { @Override public void encode(ChannelHandlerContext ctx, Integer msg, List<Object> out) throws Exception { out.add(String.valueOf(msg)); } }
===========END===========