设为首页 - 加入收藏 抚州站长网 (http://www.0794zz.com)- 国内知名站长资讯网站,提供最新最全的站长资讯,创业经验,网站建设等!
热搜: javascript vivo 2018 2008
当前位置: 首页 > 运营中心 > 网站设计 > 教程 > 正文

Netty - 粘包和半包(下)

发布时间:2019-10-27 21:20 所属栏目:[教程] 来源:健健壮
导读:接上篇《TCP 粘包和半包 介绍及解决(上)》 上一篇介绍了粘包和半包及其通用的解决方案,今天重点来看一下 Netty 是如何实现封装成帧(Framing)方案的。 解码核心流程 之前介绍过三种解码器FixedLengthFrameDecoder、DelimiterBasedFrameDecoder、LengthFiel

接上篇《TCP 粘包和半包 介绍及解决(上)》

上一篇介绍了粘包和半包及其通用的解决方案,今天重点来看一下 Netty 是如何实现封装成帧(Framing)方案的。

Netty - 粘包和半包(下)

解码核心流程

之前介绍过三种解码器FixedLengthFrameDecoder、DelimiterBasedFrameDecoder、LengthFieldBasedFrameDecoder,它们都继承自ByteToMessageDecoder,而ByteToMessageDecoder继承自ChannelInboundHandlerAdapter,其核心方法为channelRead。因此,我们来看看ByteToMessageDecoder的channelRead方法:

  1. @Override?
  2. public?void?channelRead(ChannelHandlerContext?ctx,?Object?msg)?throws?Exception?{?
  3. ?if?(msg?instanceof?ByteBuf)?{?
  4. ?CodecOutputList?out?=?CodecOutputList.newInstance();?
  5. ?try?{?
  6. ?//?将传入的消息转化为data?
  7. ?ByteBuf?data?=?(ByteBuf)?msg;?
  8. ?//?最终实现的目标是将数据全部放进cumulation中?
  9. ?first?=?cumulation?==?null;?
  10. ?//?第一笔数据直接放入?
  11. ?if?(first)?{?
  12. ?cumulation?=?data;?
  13. ?}?else?{?
  14. ?//?不是第一笔数据就进行追加?
  15. ?cumulation?=?cumulator.cumulate(ctx.alloc(),?cumulation,?data);?
  16. ?}?
  17. ?//?解码?
  18. ?callDecode(ctx,?cumulation,?out);?
  19. ?}?
  20. ?//?以下代码省略,因为不属于解码过程?
  21. ?}?

再来看看callDecode方法:

  1. protected?void?callDecode(ChannelHandlerContext?ctx,?ByteBuf?in,?List?out)?{?
  2. ?try?{?
  3. ?while?(in.isReadable())?{?
  4. ?int?outoutSize?=?out.size();?
  5. ?if?(outSize?>?0)?{?
  6. ?//?以下代码省略,因为初始状态时,outSize?只可能是0,不可能进入这里?
  7. ?}?
  8. ?int?oldInputLength?=?in.readableBytes();?
  9. ?//?在进行?decode?时,不执行handler的remove操作。?
  10. ?//?只有当?decode?执行完之后,开始清理数据。?
  11. ?decodeRemovalReentryProtection(ctx,?in,?out);?
  12. ?//?省略以下代码,因为后面的内容也不是解码的过程?

再来看看decodeRemovalReentryProtection方法:

  1. final?void?decodeRemovalReentryProtection(ChannelHandlerContext?ctx,?ByteBuf?in,?List?out)?
  2. ?throws?Exception?{?
  3. ?//?设置当前状态为正在解码?
  4. ?decodeState?=?STATE_CALLING_CHILD_DECODE;?
  5. ?try?{?
  6. ?//?解码?
  7. ?decode(ctx,?in,?out);?
  8. ?}?finally?{?
  9. ?//?执行hander的remove操作?
  10. ?boolean?removePending?=?decodeState?==?STATE_HANDLER_REMOVED_PENDING;?
  11. ?decodeState?=?STATE_INIT;?
  12. ?if?(removePending)?{?
  13. ?handlerRemoved(ctx);?
  14. ?}?
  15. ?}?
  16. }?
  17. //?子类都重写了该方法,每种实现都会有自己特殊的解码方式?
  18. protected?abstract?void?decode(ChannelHandlerContext?ctx,?ByteBuf?in,?List?out)?throws?Exception;?

从上面的过程可以总结出,在解码之前,需要先将数据写入cumulation,当解码结束后,需要通过 handler 进行移除。

具体解码过程

刚刚说到decode方法在子类中都有实现,那针对我们说的三种解码方式,一一看其实现。

1. FixedLengthFrameDecoder

其源码为:

  1. @Override?
  2. protected?final?void?decode(ChannelHandlerContext?ctx,?ByteBuf?in,?List?out)?throws?Exception?{?
  3. ?Object?decodedecoded?=?decode(ctx,?in);?
  4. ?if?(decoded?!=?null)?{?
  5. ?out.add(decoded);?
  6. ?}?
  7. }?
  8. protected?Object?decode(?
  9. ?@SuppressWarnings("UnusedParameters")?ChannelHandlerContext?ctx,?ByteBuf?in)?throws?Exception?{?
  10. ?//?收集到的数据是否小于固定长度,小于就代表无法解析?
  11. ?if?(in.readableBytes()?
  12. ?return?null;?
  13. ?}?else?{?
  14. ?return?in.readRetainedSlice(frameLength);?
  15. ?}?
  16. }?

就和这个类的名字一样简单,就是固定长度进行解码,因此,在设置该解码器的时候,需要在构造方式里传入frameLength。

2. DelimiterBasedFrameDecoder

【免责声明】本站内容转载自互联网,其相关言论仅代表作者个人观点绝非权威,不代表本站立场。如您发现内容存在版权问题,请提交相关链接至邮箱:bqsm@foxmail.com,我们将及时予以处理。

网友评论
推荐文章