数据在网络中传输的都是二进制字节码数据,在发送数据时就需要编码,接收数据时就需要解码
codec(编解码器) 的组成部分有两个:
decoder(解码器)和 encoder(编码器)。
encoder 负责把业务数据转换成字节码数据,decoder 负责把字节码数据转换成业务数据
编解码的方式要一致, 假设你以UTF-8字符集编码成为字符串、 那解码也是需要UTF-8字符集解码成为字符串
Netty
本身的编解码编解码器负责出入站操作,那么也一定要实现
ChannelInboundHandler
、ChannelOutboundHandler
接口,所以解码器本质 上也是ChannelHandler
。
netty
中编码的抽象基类MessageToByteEncoder
、MessageToMessageEncoder
可以在其下面的继承关系查看对应提供的解码器
StringEncoder
,对字符串数据进行编码HttpObjectEncoder
http对象编码器ObjectEncoder
将对象(需要实现Serializable接⼝)编码为字节流还有很多提供的编码器, 自行从抽象基类
MessageToByteEncoder
、MessageToMessageEncoder
继承关系找
netty
中编码的抽象基类MessageToMessageDecoder
、ByteToMessageDecoder
可以在其下面的继承关系查看对应提供的解码器
StringDecoder
对字符串数据进行解码
ObjectDecoder
将对象(需要实现Serializable接⼝)解码为对象
RedisDecoder
基于Redis协议的解码器
XmlDecoder
基于XML格式的解码器
JsonObjectDecoder
基于json数据格式的解码器
HttpObjectDecoder
基于http协议的解码器
还有很多提供的编码器, 自行从抽象基类
MessageToMessageDecoder
、ByteToMessageDecoder
继承关系找
Netty中的
ObjectDecoder
、ObjectEncoder
底层使用的还是JDK的序列化方式, 缺点很多!
- 无法跨语言 …
- 易被攻击 …
- 序列化后的流太大 …
- 序列化性能太差
推荐使用扩展介绍的
Google 的 Protobuf
ChannelHandler
说明
ChannelHandler
下的ChannelInboundHandler (入站)
、ChannelOutboundHandler (出站)
Netty 的主要组件有 Channel、EventLoop、ChannelFuture、ChannelHandler、ChannelPipe 等
ChannelHandler 充当了处理入站和出站数据的应用程序逻辑的容器。
实现 ChannelInboundHandler 接口(或 ChannelInboundHandlerAdapter),你就可以接收入站事件和数据,这些数据会被业务逻辑处理。
当要给客户端 发 送 响 应 时 , 也 可 以 从 ChannelInboundHandler 冲 刷 数 据 。 业 务 逻 辑 通 常 写 在 一 个 或 者 多 个 ChannelInboundHandler 中。ChannelOutboundHandler 原理一样,只不过它是用来处理出站数据的
ChannelPipeline 提供了 ChannelHandler 链的容器。以客户端应用程序为例,如果事件的运动方向是从客户端到
服务端的,那么我们称这些事件为出站的,即客户端发送给服务端的数据会通过 pipeline 中的一系列
ChannelOutboundHandler,并被这些 Handler 处理,反之则称为入站的
本质上Netty 中的编解码器也是处理器的一种。
当 Netty 发送或者接受一个消息的时候,就将会发生一次数据转换。入站消息会被解码:从字节转换为另一种
格式(比如 java 对象); 如果是出站消息,它会被编码成字节。
Netty 提供一系列实用的编解码器,他们都实现了 ChannelInboundHadnler 或者 ChannelOutboundHandler 接口。
在这些类中,channelRead 方法已经被重写了。以入站为例,对于每个从入站 Channel 读取的消息,这个方法会
被调用。随后,它将调用由解码器所提供的 decode()方法进行解码,并将已经解码的字节转发给 ChannelPipeline
中的下一个 ChannelInboundHandler (处理器)。
ByteToMessageDecoder
学习由于不可能知道发送方是否会一次性发送一个完整的信息,tcp 有可能出现粘包拆包的问题,这个类会对入站数据进行缓冲,直到它准备好被处理.
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof ByteBuf) {
CodecOutputList out = CodecOutputList.newInstance();
try {
first = cumulation == null;
cumulation = cumulator.cumulate(ctx.alloc(),
first ? Unpooled.EMPTY_BUFFER : cumulation, (ByteBuf) msg);
callDecode(ctx, cumulation, out);
} catch (DecoderException e) {
........
本身有判断该
msg
是否需要进行解码, 如果需要解码则会调用callDecode(ctx, cumulation, out);
否则调用
ctx.fireChannelRead(msg);
传递给下一个处理器(解码器)
MessageToByteEncoder
学习 @Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
ByteBuf buf = null;
try {
if (acceptOutboundMessage(msg)) {
@SuppressWarnings("unchecked")
I cast = (I) msg;
buf = allocateBuffer(ctx, cast, preferDirect);
try {
encode(ctx, cast, buf);
........
编码器本身也有判断是否需要处理编码: 如果
msg
不满足, 则会传递给下一个处理器(编码器)
如果使用了编解码器, 或者处理器, 泛型指定了类型 , 如:
MessageToByteEncoder
那么只有
msg
是string类型时候才会进行处理编解码。 否则传递给下一个处理器, 最终输出/输入
Protobuf
(参考文档 、其他作者介绍 、其他作者的入门案例)Protobuf 是 Google 发布的开源项目,全称 Google Protocol Buffers,是一种轻便高效的结构化数据存储格式,
可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC[远程过程调用 remote procedure call ] 数据交换格式 。
Protobuf 是以 message 的方式来管理数据的
支持跨平台、跨语言,即[客户端和服务器端可以是不同的语言编写的] (支持目前绝大多数语言,例如 C++、C#、Java、python 等)
高性能,高可靠性 ,
使用 protobuf 编译器能自动生成代码,Protobuf 是将类的定义使用.proto 文件进行描述。
说明,在 idea 中编写 .proto 文件时,会自动提示是否下载 .ptotot 编写插件. 可以让语法高亮。 然后通过 protoc.exe 编译器根据.proto 自动生成.java 文件
使用方式:
.proto
。Netty
使用Protobuf
作为传输数据格式需要做的东西<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.12.0</version>
</dependency>
maven_pom文件加上
<plugin>
<groupId>org.xolstice.maven.pluginsgroupId>
<artifactId>protobuf-maven-pluginartifactId>
<version>0.6.1version>
<configuration>
<protocArtifact>
com.google.protobuf:protoc:3.12.0:exe:${os.detected.classifier}
protocArtifact>
<pluginId>grpc-javapluginId>
configuration>
plugin>
pipeline.addLast(new ProtobufDecoder()); // 服务端解码器
pipeline.addLast(new ProtobufEncoder()); // 客户端编码器
ReplayingDecoder 不需要判断数据是否足够读取,内部会进行处理判断,
并 不 是 所 有 的 ByteBuf 操 作 都 被 支 持 , 如 果 调 用 了 一 个 不 被 支 持 的 方 法 , 将 会 抛 出 一 个
UnsupportedOperationException。
ReplayingDecoder 在某些情况下可能稍慢于 ByteToMessageDecoder,例如网络缓慢并且消息格式复杂时,
消息会被拆成了多个碎片,速度变慢
1