Netty私有协议栈踩

最近在学习netty框架,参考的是《Netty权威指南》。对于一个刚接触这类框架的新手来说,的确是一本可以给我们指引方向的好书。但是前几天在看完netty私有协议栈的构建,动手敲完代码之后,发现踩了很多坑,刚好借此查看了netty源码,补充一波知识。来来来,干货上来。

netty代码的具体实现就不贴出来了,主要分享几个大家在阅读该节时需要注意的地方。

1、首先是netty编码器的问题。书上的类名为NettyMessageEncoder

该类是继承自MessageToMessage<T>,泛型类在我们这个协议中自然是消息的载体NettyMessage,重写的encode方法签名如下

	protected void encode(ChannelHandlerContext channelHandlerContext, NettyMessage nettyMessage, List<Object> list) throws Exception{}
大家注意最后一个入参是List<Object>,关注下这个,因为这是后面的一个坑

根据书上的代码,该方法就是将nettyMessage对象序列化成byte数组,最后将其构造成ByteBuf对象,由ChannelHandlerContext负责运输。一开始我也是参照书上的代码敲了,但是很疑惑的地方在于,我起到该方法的最后一行

sendBuf.setInt(4,sendBuf.readableBytes());
完成之后,然后运行客户端时,server一直无法接受消息。之后打了断点进去看源代码,发现在父类的
MessageToMessageEncoder<I>的write方法中,通过多态调用了子类的encode方法,如下

所以当我们的编码类执行完encode之后,会返回到父类中继续执行,代码如下

注意看花圈的地方,第一个是out入参,就是之前提到的List<Object>这个对象,但是参照书上的,子类方法最后一行是setInt,并没有给list赋值,所以在这里是空的,报错。

	解决了发送问题之后,客户端可以发送了,在服务端,解码器是继承类LenthFieldBasedDecoder类,所以子类的decode方法需要重写父类的方法。
首先该方法是参照书上的代码。但是问题又来了,我decode方法收到消息开始解码之后,第一行代码调用父类的decode方法

一直返回的是null,又得打断点进去,其实父类里面对这个是ByteBuf其实是做了处理的。
1、我们在服务端构造nettyMessageDecoder的代码如下

注意红圈中的两个数字,之后分析父类代码时会用到。
2、NettyMessageDecoder的构造器及实现如下

父类的具体构造器中部分代码如下

3、父类的decode方法中

第一行是获取头部长度字段的偏移量,第二行获取bytebuf中实际的数据长度
然后如果frameLength长度大于0的话,再次计算值

这一行代码具体什么意思,暂时还没理解,环境大家指教,注意lengthFieldEndOffet的值就是上面红线椭圆圈里面的取值。
父类的decode执行到最后

重新构造了一个ByteBuf,这个其实就是客户端发送的NettyMessage的对象字节数组。readerIndex=0,writeIndex=frameLength
但是实际上书中的客户端encode最后一行代码是,它的意思是在byte数组的低4个字节处设置NettyMessage的实际字节长度
sendBuf.setInt(4,sendBuf.readableBytes());
在执行到LengthFieldBaseFrameDecoder的decode方法中时



该行代码返回的是true,暨实际可读字节数小于总帧数,想到在FrameLengthInt=frameLength的值,并且frameLength的长度是原来的字节长度加上如下字段
this.lengthFieldEndOffset = lengthFieldOffset + lengthFieldLength;
frameLength += (long)(this.lengthAdjustment + this.lengthFieldEndOffset);
所以是+8,就是NettyMessageDecoder的构造器中的最后两个参数4+4,所以我们在客户端encode方法的

sendBuf.setInt(4,sendBuf.readableBytes());
应该改为

sendBuf.setInt(4,sendBuf.readableBytes()-8);
好了,到此第二个坑补完了

第三个坑。
参照书上的代码,在NettymessageDecoder的decode方法中需要对ByteBuf进行读取,但是在读的时候一直说下标越界,查看源代码,在AbstractByteBuf中,

根源实在红圈内,但是为什么呢?后来看了断点里面,发现书上写的代码是这样的

首先是将入参byteBuf传入父类的decode方法,然后返回一个新的ByteBuf就是之前我们看到的

因为父类这样调用,所以在执行了extractFrame后,ByteBuf的readerIndex=writeIndex了,所以在子类的decode中再调用byteBuf实例的readInt方法,根据
else if(this.readerIndex > this.writerIndex - minimumReadableBytes)
自然是返回的true,所以会抛异常,所以最后在子类的decode中,在调用父类的decode方法得到返回的ByteBuf新对象之后,子类在读取字节时,应该使用返回的新的ByteBuf,

而不是入参中的byteBuf。
至此,三个坑全部踩完了,写的有点粗糙,需要结合代码实际看。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值