编码器
回顾一下我们先前的定义,编码器实现了ChannelOutboundHandler,并将出站数据从一种格式转换为另一种格式,和我们方才学习的解码器的功能正好相反。
Netty 提供了一组类,用于帮助你编写具有以下功能的编码器:
-
将消息编码为字节;
-
将消息编码为消息
抽象类 MessageToByteEncoder
我们将首先从抽象基类 MessageToByteEncoder 开始来对这些类进行考察。
前面我们看到了如何使用ByteToMessageDecoder 来将字节转换为消息。
API
现在我们将使用 MessageToByteEncoder 来做逆向的事情。
表10-3 展示了该API。
- 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 occurs
*/
protected abstract void encode(ChannelHandlerContext ctx, I msg, ByteBuf out) throws Exception;
和解码器的不同
你可能已经注意到了,这个类只有一个方法,而解码器有两个。
原因是解码器通常需要在Channel 关闭之后产生最后一个消息(因此也就有了decodeLast()方法)。
这显然不适用于编码器的场景——在连接被关闭之后仍然产生一个消息是毫无意义的。
示例代码
图10-3 展示了ShortToByteEncoder,其接受一个Short 类型的实例作为消息,将它编码为Short 的原子类型值,并将它写入ByteBuf 中,其将随后被转发给ChannelPipeline 中的下一个ChannelOutboundHandler。
- 流程
出站 Short==>编码为 ByteBuf ==> [1][2]==》写入到 ChannelOutbounderHandler
每个传出的Short 值都将会占用ByteBuf 中的2 字节。
ShortToByteEncoder 的实现如代码清单10-5 所示。
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
/**
* short->byte 编码器
* @author binbin.hou
* @since 0.0.1
*/
public class ShortToByteEncoder extends MessageToByteEncoder<Short> {
@Override
protected void encode(ChannelHandlerContext ctx, Short msg, ByteBuf out) throws Exception {
// 将 byte 信息写入
out.writeShort(msg);
}
}
Netty 提供了一些专门化的MessageToByteEncoder,你可以基于它们实现自己的编码器。
WebSocket08FrameEncoder 类提供了一个很好的实例。你可以在io.netty.handler.codec.http.websocketx 包中找到它。
抽象类MessageToMessageEncoder
你已经看到了如何将入站数据从一种消息格式解码为另一种。
MessageToMessageEncoder 类的 encode()方法提供了这种能力。
接口
public abstract class MessageToMessageEncoder<I> extends ChannelOutboundHandlerAdapter {}
API
/**
* 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 aggregation
* @throws Exception is thrown if an error occurs
*/
protected abstract void encode(ChannelHandlerContext ctx, I msg, List<Object> out) throws Exception;
示例代码
编码器将每个出站 Integer 的 String 表示添加到了该List 中。
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.handler.codec.MessageToMessageEncoder;
import java.util.List;
/**
* 整形->字符串 编码器
* @author binbin.hou
* @since 0.0.1
*/
public class IntegerToStringEncoder extends MessageToMessageEncoder<Integer> {
@Override
protected void encode(ChannelHandlerContext ctx, Integer msg, List<Object> out) throws Exception {
out.add(msg.toString());
}
}
更多信息
关于有趣的 MessageToMessageEncoder 的专业用法,请查看io.netty.handler.codec.protobuf.ProtobufEncoder 类
它处理了由Google 的Protocol Buffers 规范所定义的数据格式。
参考资料
《Netty in Action》 P150
- Other