《Netty实战》读书笔记——编解码器与网络协议
编解码器
解码器
- 将字节解码为消息——
ByteToMessageDecoder
和ReplayingDecoder
; - 将一种消息类型解码为另一种——
MessageToMessageDecoder
io.netty.handler.codec.LineBasedFrameDecoder
:这个类在Netty 内部也有使用,它使用了行尾控制字符(\n
或者\r\n
)来解析消息数据;
io.netty.handler.codec.http.HttpObjectDecoder
:一个HTTP 数据的解码器。在io.netty.handler.codec
子包下面,你将会发现更多用于特定用例的编码器和解码器实现。
Netty 提供了TooLongFrameException
类,其将由解码器在帧超出指定的大小限制时抛出。
编码器
MessageToByteEncoder
:encode()
方法是你需要实现的唯一抽象方法。它被调用时将会传入要被该类编码为ByteBuf
的出站消息。该ByteBuf 随后将会被转发给ChannelPipeline
中的下一个ChannelOutboundHandler
MessageToMessageEncoder
:出站数据将如何从一种消息编码为另一种
编解码器
ByteToMessageCodec
:我们需要将字节解码为某种形式的消息,可能是POJO
,随后再次对它进行编码。ByteToMessageCodec
将为我们处理好这一切,因为它结合了ByteToMessageDecoder
以及它的逆向——MessageToByteEncoder
MessageToMessageCodec
:将一种消息格式转换为另外一种消息格式,可以在一个单个的类中实现该转换的往返过程
CombinedChannelDuplexHandler
:这个类充当了ChannelInboundHandler
和ChannelOutboundHandler
的容器。通过提供分别继承了解码器类和编码器类的类型,我们可以实现一个编解码器,而又不必直接扩展抽象的编解码器类。
Web协议
SslChannelHandler
public class SslChannelInitializer extends ChannelInitializer<Channel> {
private final SslContext context;
private final boolean startTls;
public SslChannelInitializer(SslContext context, boolean startTls) {
// 传入要使用的SslContext
this.context = context;
// 如果设置为true,第一个写入的消息将不会被加密(客户端应该设置为true)
this.startTls = startTls;
}
@Override
protected void initChannel(Channel ch) throws Exception {
// 对于每个SslHandler实例,都从SslContext获取一个新的SSLEngine
SSLEngine engine = context.newEngine(ch.alloc());
// 将SslHandler作为第一个ChannelHandler添加到ChannelPipeline中
ch.pipeline().addFirst("ssl", new SslHandler(engine, startTls));
}
}
HttpObjectAggregator
:聚合HTTP消息
HTTP压缩:
HttpContentDecompressor
(客户端使用)HttpContentCompressor
(服务端使用)
public class HttpPipelineInitializer extends ChannelInitializer<Channel> {
private final boolean isClient;
public HttpPipelineInitializer(boolean isClient) {
this.isClient = isClient;
}
@Override
protected void initChannel(Channel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
// 方法一:分别添加编码器与解码器
if (isClient) {
pipeline.addLast("decoder", new HttpResponseDecoder());
pipeline.addLast("encoder", new HttpRequestEncoder());
} else {
pipeline.addLast("decoder", new HttpRequestDecoder());
pipeline.addLast("encoder", new HttpResponseEncoder());
}
// 方法二:直接添加编解码器
if (isClient) {
pipeline.addLast("codec", new HttpClientCodec());
} else {
pipeline.addLast("codec", new HttpServerCodec());
}
// 聚合HTTP消息
pipeline.addLast("aggregator", new HttpObjectAggregator(512 * 1024));
// HTTP压缩
if (isClient) {
pipeline.addLast("decompressor", new HttpContentDecompressor());
} else {
pipeline.addLast("compressor", new HttpContentCompressor());
}
}
}
使用HTTPS:启用HTTPS 只需要将SslHandler
添加到ChannelPipeline
的ChannelHandler
组合中。
WebSocket
:要想向你的应用程序中添加对于WebSocket
的支持,你需要将适当的客户端或者服务器WebSocketChannelHandler
添加到ChannelPipeline
中。这个类将处理由WebSocket
定义的称为帧的特殊消息类型。如表11-3 所示,WebSocketFrame
可以被归类为数据帧或者控制帧。
// 如果被请求的端点是/websocket 则处理该升级握手
pipeline.addLast(new WebSocketServerProtocolHandler("/websocket"));
空闲的连接,心跳消息
public class IdleStateHandlerInitializer extends ChannelInitializer<Channel> {
@Override
protected void initChannel(Channel ch) throws Exception {
// 当连接空闲时间太长时,将会触发一个IdleStateEvent事件
ch.pipeline().addLast(new IdleStateHandler(0, 0, 60, TimeUnit.SECONDS));
ch.pipeline().addLast(new HeartbeatHandler());
}
public static final class HeartbeatHandler extends ChannelInboundHandlerAdapter {
private static final ByteBuf HEARTBEAT_SEQUENCE = Unpooled.unreleasableBuffer(Unpooled.copiedBuffer("HEARTBEAT", CharsetUtil.ISO_8859_1));
// 在ChannelInboundHandler中年重写userEventTriggered方法来处理IdleStateEvent事件
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
// 发送心跳消息,并在发送失败时关闭该连接
ctx.writeAndFlush(HEARTBEAT_SEQUENCE.duplicate())
.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
} else {
super.userEventTriggered(ctx, evt);
}
}
}
}