深入理解Netty编解码、粘包拆包、心跳机制( 三 )
用法很简单 , 发送方加上编码器:
@Overrideprotected void initChannel(SocketChannel ch) throws Exception { //添加编码器 , 使用默认的符号\n , 字符集是UTF-8ch.pipeline().addLast(new LineEncoder(LineSeparator.DEFAULT, CharsetUtil.UTF_8));ch.pipeline().addLast(new TcpClientHandler());}
接收方加上解码器:
@Overrideprotected void initChannel(SocketChannel ch) throws Exception { //解码器需要设置数据的最大长度 , 我这里设置成1024 ch.pipeline().addLast(new LineBasedFrameDecoder(1024)); //给pipeline管道设置业务处理器 ch.pipeline().addLast(new TcpServerHandler());}
然后在发送方 , 发送消息时在末尾加上标识符:
@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {for (int i = 1; i <= 5; i++) {//在末尾加上默认的标识符\nByteBuf byteBuf = Unpooled.copiedBuffer("msg No" + i + StringUtil.LINE_FEED, Charset.forName("utf-8"));ctx.writeAndFlush(byteBuf); }}
于是我们再次启动服务端和客户端 , 在服务端的控制台可以看到:
文章插图
在数据的末尾添加特殊的符号标识数据包的边界 , 粘包、拆包的问题就得到解决了 。
注意:数据末尾一定是分隔符 , 分隔符后面不要再加上数据 , 否则会当做下一条数据的开始部分 。 下面是错误演示:
@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {for (int i = 1; i <= 5; i++) {//在末尾加上默认的标识符\nByteBuf byteBuf = Unpooled.copiedBuffer("msg No" + i + StringUtil.LINE_FEED + "[我是分隔符后面的字符串]", Charset.forName("utf-8"));ctx.writeAndFlush(byteBuf); }}
服务端的控制台就会看到这样的打印信息:
文章插图
使用自定义长度帧解码器使用这个解码器解决粘包问题的原理是上面讲的第二种 , 在数据的头部声明数据的长度 , 按长度获取数据 。 这个解码器构造器需要定义5个参数 , 相对较为复杂一点 , 先看参数的解释:
- maxFrameLength 发送数据包的最大长度
- lengthFieldOffset 长度域的偏移量 。 长度域位于整个数据包字节数组中的开始下标 。
- lengthFieldLength 长度域的字节数长度 。 长度域的字节数长度 。
- lengthAdjustment 长度域的偏移量矫正 。 如果长度域的值 , 除了包含有效数据域的长度外 , 还包含了其他域(如长度域自身)长度 , 那么 , 就需要进行矫正 。 矫正的值为:包长 - 长度域的值 – 长度域偏移 – 长度域长 。
- initialBytesToStrip 丢弃的起始字节数 。 丢弃处于此索引值前面的字节 。
文章插图
矫正偏移量是什么意思呢?
是假设你的长度域设置的值除了包括有效数据的长度还有其他域的长度包含在里面 , 那么就要设置这个值进行矫正 , 否则解码器拿不到有效数据 。
丢弃的起始字节数 。 这个比较简单 , 就是在这个索引值前面的数据都丢弃 , 只要后面的数据 。 一般都是丢弃长度域的数据 。 当然如果你希望得到全部数据 , 那就设置为0 。
下面就在消息接收端使用自定义长度帧解码器 , 解决粘包的问题:
@Overrideprotected void initChannel(SocketChannel ch) throws Exception { //数据包最大长度是1024//长度域的起始索引是0//长度域的数据长度是4//矫正值为0 , 因为长度域只有 有效数据的长度的值//丢弃数据起始值是4 , 因为长度域长度为4 , 我要把长度域丢弃 , 才能得到有效数据ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(1024, 0, 4, 0, 4));ch.pipeline().addLast(new TcpClientHandler());}
- 全新8核国产CPU深入探秘:马上能买到
- 数据|新基建时代,高大全的数据管理解决方案是怎样“炼”成的?
- 16G运存+256G内存,专业骁龙865旗舰,性价比深入人心
- 绿色骑行深入校园,共享单车长途长时需求量提升
- 不被理解的超时代发明,你知道几个?在线膜拜大神,西瓜视频真相
- 深入调查SolarWinds黑客事件 微软已查封一个核心服务器
- 《深入理解Java虚拟机》:Java内存区域
- 基于Netty高性能RPC框架Nifty协议、传输层、编解码
- 深入探讨 JavaScript 逻辑赋值运算符
- 彻底理解 IO 多路复用实现机制