概述
一个问题
编码器实现了ChannelOutboundHandler
,并将出站数据从 一种格式转换为另一种格式,和我们方才学习的解码器的功能正好相反。Netty 提供了一组类, 用于帮助你编写具有以下功能的编码器:
- 将消息编码为字节
- 将消息编码为消息
我们将首先从抽象基类 MessageToByteEncoder 开始来对这些类进行考察
1 抽象类 MessageToByteEncoder
解码器通常需要在Channel
关闭之后产生最后一个消息(因此也就有了 decodeLast()
方法)
这显然不适于编码器的场景——在连接被关闭之后仍然产生一个消息是毫无意义的
1.1 ShortToByteEncoder
其接受一Short
型实例作为消息,编码为Short
的原子类型值,并写入ByteBuf
,随后转发给ChannelPipeline
中的下一个 ChannelOutboundHandler
每个传出的 Short 值都将会占用 ByteBuf 中的 2 字节
1.2 Encoder
Netty 提供了一些专门化的 MessageToByteEncoder
,可基于此实现自己的编码器
WebSocket08FrameEncoder
类提供了一个很好的实例
2 抽象类 MessageToMessageEncoder
你已经看到了如何将入站数据从一种消息格式解码为另一种
为了完善这幅图,将展示 对于出站数据将如何从一种消息编码为另一种。MessageToMessageEncoder
类的 encode()
方法提供了这种能力
为了演示,使用IntegerToStringEncoder
扩展了 MessageToMessageEncoder
- 编码器将每个出站 Integer 的 String 表示添加到了该 List 中
关于有趣的 MessageToMessageEncoder 的专业用法,请查看 io.netty.handler. codec.protobuf.ProtobufEncoder
类,它处理了由 Google 的 Protocol Buffers 规范所定义 的数据格式。
一个java对象最后是如何转变成字节流,写到socket缓冲区中去的
writeAndFlush
- 从tail节点开始往前传播
- 逐个调用channelHandler#write
- 逐个调用channelHandler#flush
java对象编码过程
write:写队列
flush:刷新写队列
writeAndFlush: 写队列并刷新
pipeline中的标准链表结构
数据从head节点流入,先拆包,然后解码成业务对象,最后经过业务Handler处理,调用write,将结果对象写出去
而写的过程先通过tail节点,然后通过encoder节点将对象编码成ByteBuf,最后将该ByteBuf对象传递到head节点,调用底层的Unsafe写到JDK底层管道
Java对象编码过程
为什么我们在pipeline中添加了encoder节点,java对象就转换成netty可以处理的ByteBuf,写到管道里?
我们先看下调用write的code
业务处理器接受到请求之后,做一些业务处理,返回一个user
- 然后,user在pipeline中传递
情形一
情形二
handler 如果不覆盖 flush 方法,就会一直向前传递直到 head 节点
落到 Encoder
节点,下面是 Encoder
的处理流程
按照简单自定义协议,将Java对象 User 写到传入的参数 out中,这个out到底是什么?
需知User
对象,从BizHandler
传入到 MessageToByteEncoder
时,首先传到 write