在设计netty的编解码器过程中,有许多组件可以选择,这里由于咱对Protostuff比较熟悉,所以就用这个组件了。由于数据要在网络上传输,所以在发送方需要将类对象转换成二进制,接收方接收到数据后,需要将二进制转换成类对象,由于这个操作在之前的文章中有讲解过:网络传输数据序列化工具Protostuff,所以可以翻看我之前的文章来查看具体的实践方法:publicclassSerializeUtil{ privatestaticclassSerializeData{ privateO } SuppressWarnings(unchecked) publicstaticbyte〔〕serialize(Objectobject){ SerializeDataserializeDatanewSerializeData(); serializeData。 CSerializeDserializeDataClass(CSerializeD)serializeData。getClass(); LinkedBufferlinkedBufferLinkedBuffer。allocate(10244); try{ SSerializeDschemaRuntimeSchema。getSchema(serializeDataClass); returnProtostuffIOUtil。toByteArray(serializeData,schema,linkedBuffer); }catch(Exceptione){ thrownewIllegalStateException(e。getMessage(),e); }finally{ linkedBuffer。clear(); } } SuppressWarnings(unchecked) TTdeserialize(byte〔〕data,CTclazz){ try{ SSerializeDschemaRuntimeSchema。getSchema(SerializeData。class); SerializeDataserializeDataschema。newMessage(); ProtostuffIOUtil。mergeFrom(data,serializeData,schema); return(T)serializeData。 }catch(Exceptione){ thrownewIllegalStateException(e。getMessage(),e); } } } 但是,上面只是普通的操作Util,如何让数据能够在netty上进行传输呢? 在netty中,如果想发送数据出去,那么需要将数据转换成二进制,然后通过网络传送出去,他提供了MessageToByteEncoder的操作类,用户需要继承此类,然后实现encode方法就可以了。来看看我们如何将我们写好的SerializeUtil操作类集成进去:publicclassNettyMessageEncoderextendsMessageToByteENettyM{ Override protectedvoidencode(ChannelHandlerContextctx,NettyMessagemsg,ByteBufout)throwsException{ out。writeBytes(SerializeUtil。serialize(msg)); } } 如上代码所示,我们就准备好了一个基于Protostuff组件实现的编码类了。编码后的数据,被添加到ByteBuf缓冲区后,被发送出去。 那么如何来实现解码器呢?publicclassNettyMessageDecoderextendsLengthFieldBasedFrameDecoder{ publicNettyMessageDecoder(intmaxFrameLength,intlengthFieldOffset,intlengthFieldLength){ super(maxFrameLength,lengthFieldOffset,lengthFieldLength); } Override publicObjectdecode(ChannelHandlerContextctx,ByteBufin)throwsException{ try{ byte〔〕dstBytesnewbyte〔in。readableBytes()〕; in。getBytes(in。readerIndex(),dstBytes); 切记这里一定要用readBytes,不能用getBytes,否则会导致readIndex不能向后移动,从而导致nettydidnotreadanythingbutdecodedamessage。错误 in。readBytes(dstBytes,0,in。readableBytes()); NettyMessagenettyMessageSerializeUtil。deserialize(dstBytes,NettyMessage。class); returnnettyM }catch(Exceptione){ System。out。println(exceptionwhendecoding:e); } } } 如上代码所示。一般情况下,需要继承netty中的ByteToMessageDecoder操作类来实现,但是考虑到这样的话需要用户自己来处理粘包拆包问题,比较麻烦,所以我们就继承自netty中为我们准备好的LengthFieldBasedFrameDecoder来进行,由于此decoder具有处理粘包拆包的功能,而且其继承自ByteToMessageDecoder类,所以就省去了我们处理粘包拆包的逻辑。 需要注意的是,在进行解码的过程中,我们首先需要从缓冲区读取数据到byte数组中,然后需要将readerIndex标记往后移动,如果读完后不移动的话,会报nettydidnotreadanythingbutdecodedamessage的错误,而且这个错误在你运行的时候并不会抛出来,非常隐蔽,要不是细细的调试客户端,根本不能发觉此错误的存在。 所以从上面代码可以看出,ByteBuf。getBytes,只是单纯的读取缓存区数据,并不会将readerIndex后移。但是ByteBuf。readBytes则会将readerIndex后移。这点必须重视。 最后,我们将这两个实现类放到handler执行容器中即可。channel。pipeline()。addLast(nettyMessageDecoder,newNettyMessageDecoder(10241024,4,4)); channel。pipeline()。addLast(nettyMessageEncoder,newNettyMessageEncoder()); channel。pipeline()。addLast(readTimeoutHandler,newReadTimeoutHandler(50)); channel。pipeline()。addLast(loginAuthResponseHandler,newLoginAuthResponseHandler()); channel。pipeline()。addLast(heartBeatHandler,newHeartBeatResponseHandler()); 最后启动服务,我们就可以看到我们的编解码器正常跑起来了:Loginisok:NettyMessage〔headerHeader〔crcCode1410399999,length0,sessionId0,type4,priority0,attachment{}〕〕 Clientsendheartbeatmessagetoserver:NettyMessage〔headerHeader〔crcCode1410399999,length0,sessionId1344,type5,priority0,attachment{}〕〕 Clientreceiveserverheartbeatmessage:NettyMessage〔headerHeader〔crcCode1410399999,length0,sessionId0,type6,priority0,attachment{}〕〕 欢迎工作一到五年的Java工程师朋友们加入Java程序员开发:854393687 群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用没有时间来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!