「这是我参与11月更文挑战的第27天,活动详情查看:2021最后一次更文挑战」。
点赞再看,养成习惯👏👏
Netty编解码
Netty涉及到编解码的组件有 Channel、ChannelHandler、ChannelPipe等,先大概了解下这几个组件的作用。
ChannelHandler
ChannelHandler 充当了处理入站和出站数据的应用程序逻辑容器。例如,实现 ChannelInboundHandler 接口(或 ChannelInboundHandlerAdapter),你就可以接收入站事件和数据,这些数据随后会被你的应用程序的业务逻辑处理。当你要给连接的客户端发送响应时,也可以从 ChannelInboundHandler 冲刷数据。你的业务逻辑通常写在一个或者多个 ChannelInboundHandler 中。ChannelOutboundHandler 原理一样,只不过它是用来处理出站数据的。
ChannelPipeline
ChannelPipeline 提供了 ChannelHandler链 的容器。以客户端应用程序为例,如果事件的运动方向是从客户端到服务端的,那么我们称这些事件为出站的,即客户端发送给服务端的数据会通过 pipeline 中的一系列ChannelOutboundHandler(ChannelOutboundHandler 调用是从 tail 到 head 方向逐个调用每个handler的逻辑) ,并被这些 Handler 处理,反之则称为入站的,入站只调用 pipeline 里的 ChannelInboundHandler 逻辑(ChannelInboundHandler 调用是从 head 到 tail 方向逐个调用每个handler的逻辑)。
当你通过 Netty 发送或者接受一个消息的时候,就将会发生一次数据转换。入站消息会被解码:从字节转换为另一种格式(比如 java 对象);如果是出站消息,它会被编码成字节。
Netty 提供了一系列实用的编码解码器,他们都实现了 ChannelInboundHadnler 或者 ChannelOutboundHandler 接口。在这些类中,channelRead方法已经被重写了。以入站为例,对于每个从入站Channel 读取的消息,这个方法会被调用。随后,它将调用由已知解码器所提供的 decode() 方法进行解码,并将已经解码的字节转发给 ChannelPipeline 中的下一个 ChannelInboundHandler。
Netty提供了很多编解码器,比如编解码字符串的StringEncoder和StringDecoder,编解码对象的ObjectEncoder和ObjectDecoder等。
如果要实现高效的编解码可以用 protobuf,但是 protobuf 需要维护大量的proto文件比较麻烦,现在一般可以使用 protostuff。
protostuff 是一个基于protobuf实现的序列化方法,它较于 protobuf 最明显的好处是,在几乎不损耗性能的情况下做到了不用我们写 .proto 文件来实现序列化。
Netty粘包拆包
TCP 是一个流协议,就是没有界限的一长串二进制数据。TCP 作为传输层协议并不不了解上层业务数据的具体含义,它会根据 TCP 缓冲区的实际情况进行数据包的划分,所以在业务上认为是一个完整的包,可能会被 TCP 拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这就是所谓的 TCP 粘包和拆包问题。面向流的通信是无消息保护边界的。
如下图所示,client发了两个数据包D1和D2,但是server端可能会收到如下几种情况的数据。
解决方案:
1)消息定长度:传输的数据大小固定长度,例如每段的长度固定为100字节,如果不够空位补空格
2)在数据包尾部添加特殊分隔符:比如下划线,中划线等,这种方法简单易行,但选择分隔符的时候一定要注意每条数据的内部一定不能出现分隔符。
3)发送长度:发送每条数据的时候,将数据的长度一并发送,比如可以选择每条数据的前4位是数据的长度,应用层处理时可以根据长度来判断每条数据的开始和结束。
Netty提供了多个解码器,可以进行分包的操作,如下:
- LineBasedFrameDecoder(回车换行分包)
- DelimiterBasedFrameDecoder(特殊分隔符分包)
- FixedLengthFrameDecoder(固定长度报文来分包)